CASE expression ส่งคืนค่าที่ผิดเมื่อใช้ CEILING


11

ฉันพบปัญหาที่CASEนิพจน์ไม่ส่งคืนสิ่งที่ฉันคาดหวัง

ในการทดสอบฉันเพิ่มตัวแปรทศนิยมและใช้CASEนิพจน์เดียวกันกับมันและใช้งานได้ดีส่งคืนผลลัพธ์ตามที่คาดไว้ (ปัดเศษค่าขึ้นเมื่อIsGun=1แต่เมื่อฉันเรียกใช้CASEนิพจน์เดียวกันกับค่าทศนิยมอื่นมันจะส่งกลับเสมอ ค่าที่มี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 CASEนิพจน์แรกที่ใช้ตัวแปรคงที่จะส่งกลับค่าUsingVarตามที่เราคาดไว้ 12.54 และสำหรับห้าครั้งสุดท้ายมันก็คืนค่าที่เราคาดหวังด้วย 13

แต่ในCASEนิพจน์ที่สอง(ตรรกะเดียวกันทั้งหมด) PriceAdjจะใช้CEILINGฟังก์ชั่นในทุก ๆ รายการโดยไม่คำนึงว่าIsGun = 1 หรือไม่

เหตุใดแบบสอบถามจึงไม่ส่งคืนผลลัพธ์ที่คาดหวัง

ในบางส่วนของตารางที่ใช้สำหรับสหภาพดูประเภทข้อมูลสำหรับราคา 1และPrice2เป็นsmallmoneyและทศนิยม (8,2) ฉันได้เปลี่ยนพวกเขาทั้งหมดเป็นทศนิยม (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มันคืนค่าประเภทที่มีความสำคัญที่สุดของประเภทข้อมูลจากสองตัวเลือก เป็นไปไม่ได้ที่การแสดงออกใด ๆ จะคืนค่าข้อมูลชนิด X ในหนึ่งแถวและชนิดข้อมูล Y ในอีกแถวหนึ่งสำหรับคอลัมน์เดียวกัน
Martin Smith

@Daniel มีประโยชน์มากที่สุด ทันทีที่ฉันแสดง (x AS NUMERIC (8,2)) สำหรับแต่ละเอาต์พุตมันจะส่งคืนผลลัพธ์ที่ฉันค้นหา ขอบคุณมากสำหรับคุณและชุมชนนี้!
Rodney G
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.