使用 CEILING 时 CASE 表达式返回错误值

Rod*_*y G 11 sql-server datatypes t-sql type-conversion

我遇到了一个问题,即CASE表达式没有返回我期望的内容。

作为测试,我添加了一个十进制变量并CASE针对它运行了相同的表达式,它运行良好,返回了我预期的结果(在 时将值向上舍入IsGun=1。但是当我CASE针对另一个十进制值运行相同的表达式时,它总是返回CEILING()函数的值,从不返回原始值。

这是SQL代码:

DECLARE @Num decimal(8,2);
    set @Num = 12.54;
    WITH PQ AS
    ( 
        SELECT 
            UPC, 
            Price1, 
            DBID,
            AVG(Price1) OVER (PARTITION BY UPC) AS Price1Avg
        FROM
            vProducts_PriceQty_Union
    )
    SELECT 
        PQ.UPC,
        PQ.Price1,
        PQ.Price1Avg,
        (CASE WHEN p.IsGun = 1 THEN CEILING(@Num) ELSE @Num END) AS UsingVar,
        CAST(
            (CASE WHEN P.IsGun = 1 THEN CEILING(PQ.Price1Avg) ELSE PQ.Price1 END)
             AS NUMERIC(8,2))
        AS PriceAdj,
        PQ.DBID,
        P.IsGun
    FROM
        PQ
     INNER JOIN
        products P ON PQ.UPC = P.UPC
Run Code Online (Sandbox Code Playgroud)

这是结果的一个片段:

DECLARE @Num decimal(8,2);
    set @Num = 12.54;
    WITH PQ AS
    ( 
        SELECT 
            UPC, 
            Price1, 
            DBID,
            AVG(Price1) OVER (PARTITION BY UPC) AS Price1Avg
        FROM
            vProducts_PriceQty_Union
    )
    SELECT 
        PQ.UPC,
        PQ.Price1,
        PQ.Price1Avg,
        (CASE WHEN p.IsGun = 1 THEN CEILING(@Num) ELSE @Num END) AS UsingVar,
        CAST(
            (CASE WHEN P.IsGun = 1 THEN CEILING(PQ.Price1Avg) ELSE PQ.Price1 END)
             AS NUMERIC(8,2))
        AS PriceAdj,
        PQ.DBID,
        P.IsGun
    FROM
        PQ
     INNER JOIN
        products P ON PQ.UPC = P.UPC
Run Code Online (Sandbox Code Playgroud)

这是它来自vProducts_PriceQty_Union的数据:

UPC             Price1      Price1Avg   UsingVar    PriceAdj    DBID  IsGun
942000899195    14.9900     14.990000   12.54       15.00       1       0
980420671300    29.9900     29.990000   12.54       30.00       1       0
980420671310    29.9900     29.990000   12.54       30.00       1       0
980426713020    29.9900     29.990000   12.54       30.00       1       0
980426713120    29.9900     29.990000   12.54       30.00       1       0
000998622130    319.0000    319.000000  13.00       319.00      1       1
000998624730    314.0000    314.000000  13.00       314.00      1       1
000998624970    419.0000    419.000000  13.00       419.00      1       1
008244284754    1015.0000   1015.000000 13.00       1015.00     2       1
010633012288    267.0000    267.000000  13.00       267.00      6       1
Run Code Online (Sandbox Code Playgroud)

从前五个表达式中可以看出,其中IsGun = 0,CASE使用固定变量的第一个表达式返回UsingVar值,正如我们所期望的那样,12.54。对于最后五个,它也返回我们期望的值,13。

但是在第二个CASE表达式中(完全相同的逻辑),PriceAdjCEILING对它们中的每一个都使用该函数,无论IsGun = 1 与否。

为什么查询没有返回预期的结果?

在一些用于联合的表的视图的数据类型价格1Price2分别SMALLMONEY十进制(8,2) 。我已经将它们全部更改为decimal(8,2),但这并没有影响结果。

Dan*_*her 11

要重现问题:

SELECT *, (CASE
    WHEN IsGun=1 THEN CEILING(Price1Avg)
    ELSE Price1 END)
FROM (
    SELECT UPC, IsGun, Price1,
           AVG(CAST(Price1 AS numeric(8, 2))) OVER (PARTITION BY UPC) AS Price1Avg
    FROM (
        VALUES ('A', 0, 14.99),
               ('B', 0, 29.99),
               ('C', 1, 319.00),
               ('D', 1, 314.00)
        ) AS x(UPC, IsGun, Price1)
    ) AS sub;
Run Code Online (Sandbox Code Playgroud)

这里发生的是CEILING(PQ.Price1Avg)产生一个numeric(38, 0).

根据文档,输出类型CEILING()与输入具有相同的基本数据类型,尽管比例(小数位数)可能会发生变化,这就是这里发生的情况。

  • AVG()在我的测试中,该函数返回numeric(38, 6).
  • CEILING()但是,该列上的函数输出numeric(38, 0)

验证:

SELECT CEILING(CAST(123.45 AS numeric(38, 6)))
Run Code Online (Sandbox Code Playgroud)

作为一种解决方法,您可以显式转换CEILING()函数的输出,这应该会给您正确的结果:

SELECT *, (CASE
    WHEN IsGun=1 THEN CAST(CEILING(Price1Avg) AS numeric(8, 2)) -- Explicit CAST.
    ELSE Price1 END)
FROM (
    SELECT UPC, IsGun, Price1,
           AVG(CAST(Price1 AS numeric(8, 2))) OVER (PARTITION BY UPC) AS Price1Avg
    FROM (
        VALUES ('A', 0, 14.99),
               ('B', 0, 29.99),
               ('C', 1, 319.00),
               ('D', 1, 314.00)
        ) AS x(UPC, IsGun, Price1)
    ) AS sub;
Run Code Online (Sandbox Code Playgroud)

  • @AdamMartin 这不正确。`iif` 扩展为 `case`。它从两个选项中[返回具有最大数据类型优先级的类型](https://msdn.microsoft.com/en-GB/library/hh213574.aspx)。对于同一列,任何表达式都不可能在一行中返回数据类型 X,在另一行中返回数据类型 Y。 (2认同)