Вираз CASE повертає неправильне значення при використанні CEILING


11

Я зіткнувся з проблемою, де 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

Ось фрагмент результатів:

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

Ось дані, які надходять від vProducts_PriceQty_Union :

UPC             Price1  Price2  Quantity    DBID
942000899195    14.9900 0.0000  2.00        1
980420671300    29.9900 0.0000  3.00        1
980420671310    29.9900 0.0000  1.00        1
980426713020    29.9900 0.0000  2.00        1
980426713120    29.9900 0.0000  1.00        1

Як видно з першої п'ятірки, де IsGun = 0, перший CASEвираз із використанням фіксованої змінної повертає значення UsingVar як те, що ми очікували, 12,54. І для останніх п'яти він також повертає значення, на яке ми б очікували, 13.

Але у другому CASEвиразі (точно така ж логіка) PriceAdj використовує CEILINGфункцію для кожного з них, незалежно від того, IsGun = 1 чи ні.

Чому запит не повертає очікуваних результатів?

У деяких з таблиць , які використовуються для об'єднання перегляду типів даних для взнос1 і price2 були smallmoney і десятковими (8,2) . З тих пір я змінив їх на десятковий (8,2) , але це не вплинуло на результати.

Відповіді:


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;

Те , що відбувається тут є те , що CEILING(PQ.Price1Avg)виробляє numeric(38, 0).

Згідно з документацією , тип виводу CEILING()має той самий базовий тип даних, що і вхідний, хоча масштаб (кількість десяткових знаків) може змінюватися, що і відбувається тут.

  • У AVG()моїх тестах функція повертається numeric(38, 6).
  • Однак CEILING()функція цього стовпця видає numeric(38, 0):

Перевірити:

SELECT CEILING(CAST(123.45 AS numeric(38, 6)))

Як вирішення, ви можете явно перетворити результат 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;

Слід також зауважити, що оператори регістра не можуть повертати кілька різних типів, але вираз IIF може, так що це може бути доцільним.
Адам Мартін

2
@AdamMartin це невірно. iifОтримує розширюється до case. Він повертає тип з найбільшою перевагою типу даних із двох варіантів. Неможливо жодне вираз повернути дані типу X в одному рядку та тип даних Y в іншому рядку для того ж стовпця.
Мартін Сміт

@Daniel, найкорисніший. Як тільки я КАСТУЙ (x AS NUMERIC (8,2)) для кожного виводу, він повертає результат, який я шукав. Велике спасибі вам та цій громаді!
Родні Г
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.