Biểu thức CASE trả về giá trị sai khi sử dụng CEILING


11

Tôi đã gặp phải một vấn đề trong đó một CASEbiểu thức không trả về những gì tôi mong đợi.

Khi thử nghiệm, tôi đã thêm một biến thập phân và chạy cùng CASEbiểu thức với nó và nó hoạt động tốt, trả về kết quả như tôi mong đợi (làm tròn giá trị lên khi IsGun=1. Nhưng khi tôi chạy CASEbiểu thức tương tự với giá trị thập phân khác, nó luôn trả về giá trị với CEILING()hàm và không bao giờ trả về giá trị ban đầu.

Đây là mã 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

Đây là một đoạn kết quả:

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

Và đây là dữ liệu được lấy từ v Products_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

Như bạn có thể thấy từ năm đầu tiên, trong đó IsGun = 0, CASEbiểu thức đầu tiên sử dụng biến cố định trả về giá trị UseVar như những gì chúng ta mong đợi, 12,54. Và trong năm vừa qua, nó cũng trả về giá trị mà chúng ta mong đợi, 13.

Nhưng trong CASEbiểu thức thứ hai (chính xác là cùng một logic), PriceAdj sử dụng CEILINGhàm trên mỗi một trong số chúng, bất kể IsGun = 1 hay không.

Tại sao truy vấn không trả về kết quả mong đợi?

Trong một số bảng được sử dụng cho chế độ xem kết hợp, các kiểu dữ liệu cho Price1Price2smallmoneybinary (8.2 ) . Tôi đã thay đổi tất cả thành số thập phân (8.2) , nhưng điều đó không ảnh hưởng đến kết quả.

Câu trả lời:


11

Để tái tạo vấn đề:

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;

Điều gì xảy ra ở đây là CEILING(PQ.Price1Avg)sản xuất a numeric(38, 0).

Theo tài liệu , loại đầu ra của CEILING()cùng kiểu dữ liệu cơ sở với đầu vào, mặc dù thang đo (số thập phân) có thể thay đổi, đó là những gì xảy ra ở đây.

  • Các AVG()chức năng, trong các thử nghiệm của tôi, trả về numeric(38, 6).
  • Các CEILING()chức năng trên cột đó, tuy nhiên, kết quả đầu ra numeric(38, 0):

Để xác minh:

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

Như một giải pháp thay thế, bạn có thể chuyển đổi rõ ràng đầu ra của CEILING()hàm, sẽ cung cấp cho bạn kết quả chính xác:

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;

Cũng cần lưu ý rằng các báo cáo trường hợp không thể trả về nhiều loại khác nhau, nhưng một biểu thức IIF có thể, do đó có thể được khuyến khích.
Adam Martin

2
@AdamMartin không đúng. An iifđược mở rộng ra case. Nó trả về kiểu có quyền ưu tiên loại dữ liệu lớn nhất từ hai tùy chọn. Không thể có bất kỳ biểu thức nào trả về kiểu dữ liệu X trong một hàng và kiểu dữ liệu Y ở một hàng khác cho cùng một cột.
Martin Smith

@Daniel, hữu ích nhất. Ngay sau khi tôi CAST (x AS NUMERIC (8.2)) cho mỗi đầu ra, nó đã trả về kết quả mà tôi đang tìm kiếm. Rất cám ơn bạn và cộng đồng này!
Rodney G
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.