Pola ini
column = @argument OR (@argument IS NULL AND column IS NULL)
dapat diganti dengan
EXISTS (SELECT column INTERSECT SELECT @argument)
Ini akan memungkinkan Anda mencocokkan NULL dengan NULL dan akan memungkinkan mesin menggunakan indeks secara column
efisien. Untuk analisis mendalam yang sangat baik dari teknik ini, saya merujuk Anda ke artikel blog Paul White:
Karena ada dua argumen dalam kasus khusus Anda, Anda dapat menggunakan teknik pencocokan yang sama dengan @Blah
- dengan cara itu Anda akan dapat menulis ulang seluruh klausa WHERE secara lebih ringkas:
WHERE
EXISTS (SELECT a.Blah, a.VersionId INTERSECT SELECT @Blah, @VersionId)
Ini akan bekerja cepat dengan indeks aktif (a.Blah, a.VersionId)
.
Atau optimizer kueri membuatnya pada dasarnya sama?
Dalam hal ini, ya. Di semua versi (setidaknya) dari SQL Server 2005 dan seterusnya, pengoptimal dapat mengenali pola col = @var OR (@var IS NULL AND col IS NULL)
dan menggantinya dengan IS
perbandingan yang tepat . Ini bergantung pada pencocokan penulisan ulang internal, jadi mungkin ada kasus yang lebih kompleks di mana ini tidak selalu dapat diandalkan.
Dalam versi SQL Server dari 2008 SP1 CU5 inklusif , Anda juga memiliki opsi untuk menggunakan Optimasi Penempelan Parameter melalui OPTION (RECOMPILE)
, di mana nilai runtime dari setiap parameter atau variabel tertanam dalam kueri sebagai literal sebelum kompilasi.
Jadi, setidaknya untuk sebagian besar, dalam hal ini pilihannya adalah masalah gaya, meskipun INTERSECT
konstruksinya tidak dapat disangkal kompak dan elegan.
Contoh berikut menunjukkan rencana eksekusi 'sama' untuk setiap variasi (literal versus referensi variabel dikecualikan):
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);