วิธีที่ดีที่สุดในการเขียน SQL Query ที่ตรวจสอบคอลัมน์สำหรับค่าที่ไม่เป็น NULL หรือ NULL


17

ฉันมี SP พร้อมพารามิเตอร์ที่มีค่า NULL เป็นค่าเริ่มต้นจากนั้นฉันต้องการสอบถามเช่นนี้

SELECT ...
FROM ...
WHERE a.Blah = @Blah AND (a.VersionId = @VersionId OR (@VersionId IS NULL AND a.VersionId IS NULL));

WHEREตรวจสอบข้างต้นสำหรับทั้งค่าที่ไม่เป็นโมฆะและค่า NULL @VersionIdสำหรับ

มันจะดีกว่าในแง่ของประสิทธิภาพแทนที่จะใช้IFคำสั่งและทำซ้ำแบบสอบถามเป็นที่ค้นหา non-NULL และอื่นสำหรับ NULL เช่นนั้น :

IF @VersionId IS NULL BEGIN
    SELECT ...
    FROM ...
    WHERE a.Blah = @Blah AND a.VersionId IS NULL;
ELSE BEGIN
    SELECT ...
    FROM ...
    WHERE a.Blah = @Blah AND a.VersionId = @VersionId;
END

หรือเครื่องมือเพิ่มประสิทธิภาพข้อความค้นหาทำให้เป็นหลักเหมือนกันหรือไม่

UPDATE:

(หมายเหตุ: ฉันใช้ SQL Server)

(และเท่าที่ฉันรู้การใช้a.VersionId = @VersionIdทั้งสองกรณีจะไม่ทำงานใช่ไหม)



ฉันมักจะใช้สิ่งต่อไปนี้: ISNULL (a.VersionId, @VersionId) = @VersionId
628426

คำตอบ:


36

รูปแบบนี้

column = @argument OR (@argument IS NULL AND column IS NULL)

สามารถถูกแทนที่ด้วย

EXISTS (SELECT column INTERSECT SELECT @argument)

สิ่งนี้จะช่วยให้คุณจับคู่ค่า NULL กับค่า NULL และอนุญาตให้เอ็นจิ้นใช้ดัชนีcolumnอย่างมีประสิทธิภาพ สำหรับการวิเคราะห์เชิงลึกที่ยอดเยี่ยมของเทคนิคนี้ฉันแนะนำคุณไปยังบทความบล็อกของ Paul White:

เนื่องจากมีข้อโต้แย้งสองข้อในกรณีเฉพาะของคุณคุณสามารถใช้เทคนิคการจับคู่แบบเดียวกันด้วย@Blahวิธีนี้คุณจะสามารถเขียน WHERE ทั้งหมดอีกครั้งโดยย่อหรือกระชับ

WHERE
  EXISTS (SELECT a.Blah, a.VersionId INTERSECT SELECT @Blah, @VersionId)

(a.Blah, a.VersionId)นี้จะทำงานได้อย่างรวดเร็วด้วยดัชนีใน


หรือเครื่องมือเพิ่มประสิทธิภาพข้อความค้นหาทำให้เป็นหลักเหมือนกันหรือไม่

ในกรณีนี้ใช่ ในทุกรุ่น (อย่างน้อย) จาก SQL Server 2005 เป็นต้นไปเครื่องมือเพิ่มประสิทธิภาพสามารถจดจำรูปแบบcol = @var OR (@var IS NULL AND col IS NULL)และแทนที่ด้วยการISเปรียบเทียบที่เหมาะสม สิ่งนี้ขึ้นอยู่กับการจับคู่การเขียนซ้ำภายในดังนั้นอาจมีกรณีที่ซับซ้อนมากกว่านี้ซึ่งไม่น่าเชื่อถือเสมอไป

ในรุ่นของ SQL Server ตั้งแต่ 2008 SP1 CU5คุณยังมีตัวเลือกในการใช้การเพิ่มประสิทธิภาพการฝังพารามิเตอร์ด้วยOPTION (RECOMPILE)โดยที่ค่ารันไทม์ของพารามิเตอร์หรือตัวแปรใด ๆ จะถูกฝังในแบบสอบถามเป็นตัวอักษรก่อนการรวบรวม

ดังนั้นอย่างน้อยถึงขนาดใหญ่ในกรณีนี้การเลือกเป็นเรื่องของสไตล์แม้ว่าการINTERSECTก่อสร้างจะมีขนาดกะทัดรัดและสง่างามอย่างปฏิเสธไม่ได้

ตัวอย่างต่อไปนี้แสดงแผนการดำเนินการ 'เดียวกัน' สำหรับแต่ละรูปแบบ (ยกเว้นตัวอักษรและการอ้างอิงตัวแปร):

DECLARE @T AS table
(
    c1 integer NULL,
    c2 integer NULL,
    c3 integer NULL

    UNIQUE CLUSTERED (c1, c2)
);

-- Some data
INSERT @T
    (c1, c2, c3)
SELECT 1, 1, 1 UNION ALL
SELECT 2, 2, 2 UNION ALL
SELECT NULL, NULL, NULL UNION ALL
SELECT 3, 3, 3;

-- Filtering conditions
DECLARE 
    @c1 integer,
    @c2 integer;

SELECT
    @c1 = NULL,
    @c2 = NULL;

-- Writing the NULL-handling out explicitly
SELECT * 
FROM @T AS T
WHERE 
(
    T.c1 = @c1
    OR (@c1 IS NULL AND T.c1 IS NULL)
)
AND 
(
    T.c2 = @c2
    OR (@c2 IS NULL AND T.c2 IS NULL)
);

-- Using INTERSECT
SELECT * 
FROM @T AS T
WHERE EXISTS 
(
    SELECT T.c1, T.c2 
    INTERSECT 
    SELECT @c1, @c2
);

-- Using separate queries
IF @c1 IS NULL AND @c2 IS NULL
    SELECT * 
    FROM @T AS T
    WHERE T.c1 IS NULL
    AND T.c2 IS NULL
ELSE IF @c1 IS NULL
    SELECT * 
    FROM @T AS T
    WHERE T.c1 IS NULL
    AND T.c2 = @c2
ELSE IF @c2 IS NULL
    SELECT * 
    FROM @T AS T
    WHERE T.c1 = @c1
    AND T.c2 IS NULL
ELSE
    SELECT * 
    FROM @T AS T
    WHERE T.c1 = @c1
    AND T.c2 = @c2;

-- Using OPTION (RECOMPILE)
-- Requires 2008 SP1 CU5 or later
SELECT * 
FROM @T AS T
WHERE 
(
    T.c1 = @c1
    OR (@c1 IS NULL AND T.c1 IS NULL)
)
AND 
(
    T.c2 = @c2
    OR (@c2 IS NULL AND T.c2 IS NULL)
)
OPTION (RECOMPILE);
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.