Ekspresi KASUS mengembalikan nilai yang salah saat menggunakan CEILING

11

Saya mengalami masalah di mana CASEekspresi tidak mengembalikan apa yang saya harapkan.

Sebagai tes, saya menambahkan variabel desimal dan menjalankan CASEekspresi yang sama terhadapnya dan berfungsi dengan baik, mengembalikan hasilnya seperti yang saya harapkan (pembulatan nilainya ketika IsGun=1. Tapi ketika saya menjalankan CASEekspresi yang sama terhadap nilai desimal lainnya, selalu mengembalikan nilai dengan CEILING()fungsi dan tidak pernah mengembalikan nilai aslinya.

Berikut adalah kode 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

Berikut cuplikan hasilnya:

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

Dan ini data yang berasal dari 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

Seperti yang Anda lihat dari lima pertama, di mana IsGun = 0, CASEekspresi pertama menggunakan variabel tetap mengembalikan nilai UsingVar seperti yang kita harapkan, 12,54. Dan untuk lima terakhir, itu juga mengembalikan nilai yang kita harapkan, 13.

Tetapi dalam CASEekspresi kedua (logika yang persis sama), PriceAdj menggunakan CEILINGfungsi pada setiap satu dari mereka, terlepas dari apakah IsGun = 1 atau tidak.

Mengapa kueri tidak mengembalikan hasil yang diharapkan?

Dalam beberapa tabel yang digunakan untuk serikat melihat jenis data untuk Price1 dan Price2 yang smallmoney dan desimal (8,2) . Sejak itu saya mengubah semuanya menjadi desimal (8,2) , tetapi itu tidak mempengaruhi hasilnya.

Rodney G
sumber

Jawaban:

11

Untuk mereproduksi masalah:

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;

Apa yang terjadi di sini adalah yang CEILING(PQ.Price1Avg)menghasilkan a numeric(38, 0).

Menurut dokumentasi , tipe output dari CEILING()datatype basis yang sama dengan input, meskipun skala (jumlah desimal) dapat berubah, yang terjadi di sini.

  • The AVG()fungsi, dalam tes saya, kembali numeric(38, 6).
  • The CEILING()fungsi pada kolom itu, bagaimanapun, output numeric(38, 0):

Untuk memverifikasi:

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

Sebagai solusinya, Anda dapat secara eksplisit mengkonversi output CEILING()fungsi, yang akan memberi Anda hasil yang benar:

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;
Daniel Hutmacher
sumber
Harus juga dicatat bahwa pernyataan kasus tidak dapat mengembalikan beberapa tipe yang berbeda, tetapi ekspresi IIF bisa, sehingga mungkin disarankan.
Adam Martin
2
@ AdamMartin itu tidak benar. Sebuah iifakan diperluas ke case. Ini mengembalikan tipe dengan prioritas tipe data terbesar dari dua opsi. Ekspresi tipe X dalam satu baris dan baris data Y tidak mungkin dilakukan untuk baris yang lain untuk kolom yang sama.
Martin Smith
@Aniel, sangat membantu. Segera setelah saya CAST (x AS NUMERIC (8,2)) untuk setiap output, ia mengembalikan hasil yang saya cari. Terima kasih banyak untuk Anda dan komunitas ini!
Rodney G