web-dev-qa-db-fra.com

L'expression de cas rend la valeur incorrecte lorsque vous utilisez plafond

J'ai rencontré un problème dans lequel une expression CASE ne renvoie pas ce que j'attends.

En tant que test, j'ai ajouté un Decimal variable et couru la même chose CASE expression contre elle et cela fonctionne bien, renvoyant les résultats comme je m'attends (arrondir la valeur lorsque IsGun=1. Mais quand j'exécute la même chose CASE expression contre une autre valeur décimale, elle renvoie toujours la valeur avec la fonction CEILING() et ne renvoie jamais la valeur d'origine.

Voici le code 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

Voici un extrait des résultats:

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

Et voici les données qu'il vient de 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

Comme vous pouvez le voir à partir des cinq premiers, où isgun = 0, la première CASE expression à l'aide de la variable fixe renvoie le Utilisation devar valeur Ce que nous attendions, 12.54. Et pour les cinq derniers, cela renvoie également la valeur que nous attendrions, 13.

Mais dans la seconde CASE expression (exactement la même logique), le priséadj utilise la fonction CEILING sur chacun d'entre eux, que ce soit Isgun = 1 ou non.

Pourquoi la requête ne renvoie-t-elle pas les résultats attendus?

Dans certaines des tables utilisées pour l'Union, voir les types de données pour Prix1 et Prix2 était SmallMoney et Decimal (8,2). Depuis, je leur ai changé tous pour être décimal (8,2), mais cela n'a pas affecté les résultats.

11
Rodney G

Pour reproduire le problème:

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;

Ce qui se passe ici, c'est que CEILING(PQ.Price1Avg) produit un numeric(38, 0).

Selon la - Documentation , le type de sortie de CEILING() est du même type de données de base que l'entrée, bien que l'échelle (le nombre de décimales) puisse changer, ce qui se passe ici .

  • La fonction AVG(), dans mes tests, retourne numeric(38, 6).
  • La fonction CEILING() fonctionne sur cette colonne, cependant, sorties numeric(38, 0):

Vérifier:

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

En tant que solution de contournement, vous pouvez convertir explicitement la sortie de la fonction CEILING(), qui devrait vous donner les résultats corrects:

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;
11
Daniel Hutmacher