CASE
式が期待どおりの結果を返さないという問題に遭遇しました。
テストとして、私はdecimal変数を追加し、それに対して同じ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
_
IsGun = 0である最初の5つからわかるように、固定変数を使用した最初のCASE
式は、予想どおり12.54のsingVar値を返します。そして、最後の5つについては、予想される値13も返します。
しかし、2番目のCASE
式(まったく同じロジック)では、-PriceAdjは、IsGun = 1であるかどうかに関係なく、それらすべてのCEILING
関数を使用します。
クエリが予期した結果を返さないのはなぜですか?
ユニオンビューに使用される一部のテーブルでは、Price1およびPrice2のデータ型はsmallmoneyおよびdecimal(8,2 )。それ以来、すべてをdecimal(8,2)に変更しましたが、結果に影響はありませんでした。
問題を再現するには:
_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)
を生成することです。
documentation によると、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;
_