CEILINGを使用するとCASE式が誤った値を返す


11

CASE式が期待どおりの結果を返さないという問題に遭遇しました。

テストとして、10進数の変数を追加して同じCASE式を実行し、期待どおりに結果を返します(IsGun=1ただし、の場合は値を切り上げます。ただし、同じCASE式を別の10進数の値に対して実行すると、常に戻ります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

最初の5つ(IsGun = 0)からわかるようにCASE、固定変数を使用する最初の式は、予想どおり12.54のUsingVar値を返します。また、最後の5つについては、予想される値である13も返します。

しかし、第二にCASE式(全く同じロジック)、PriceAdjは使用CEILINGにかかわらずかどうか、それらのすべての単一のものにする機能をIsGunが 1か否かを=。

クエリが予期した結果を返さないのはなぜですか?

ユニオンビューに使用される一部のテーブルでは、Price1およびPrice2のデータ型はsmallmoneyおよびdecimal(8,2)でした。その後、すべてをdecimal(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;

また、caseステートメントは複数の異なる型を返すことはできませんが、IIF式は返すことができるため、推奨される場合があります。
Adam Martin

2
不正解です@AdamMartin。アンはiifに出て拡張しますcase。これは、最大のデータ・タイプの優先順位と型を返す二つの選択肢から。同じ列の1つの行でデータ型Xを返し、別の行でデータ型Yを返す式はありません。
マーティン・スミス

@ダニエル、最も役に立った。各出力に対してCAST(x AS NUMERIC(8,2))を実行するとすぐに、探していた結果が返されました。あなたとこのコミュニティに感謝します!
Rodney G
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.