Cara terbaik untuk menulis SQL Query yang memeriksa kolom untuk nilai NULL atau NULL

17

Saya memiliki SP dengan parameter yang memiliki NULL sebagai nilai default dan kemudian saya ingin melakukan kueri seperti ini:

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

The WHEREatas pemeriksaan untuk kedua nilai non-NULL dan nilai NULL untuk @VersionId.

Apakah lebih baik dalam hal kinerja daripada menggunakan IFpernyataan dan menduplikasi kueri menjadi yang mencari NULL dan yang lain untuk NULL seperti itu? :

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

Atau optimizer kueri membuatnya pada dasarnya sama?

MEMPERBARUI:

(Catatan: Saya menggunakan SQL Server)

(Dan sejauh yang saya tahu, menggunakan a.VersionId = @VersionIduntuk kedua kasus tidak akan berhasil, kan?)

pengguna2173353
sumber
2
Artikel terkait: Parameter Sniffing, Embedding, dan Opsi
RECOMPILE
Saya biasanya menggunakan yang berikut ini: ISNULL (a.VersionId, @VersionId) = @VersionId
628426

Jawaban:

36

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 columnefisien. 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 ISperbandingan 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 INTERSECTkonstruksinya 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);
Andriy M
sumber