Bir sütunu NULL olmayan değer veya NULL için denetleyen SQL Query yazmanın en iyi yolu


17

Varsayılan değer olarak NULL olan bir parametre ile bir SP var ve sonra böyle bir sorgu yapmak istiyorum:

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

WHERENULL olmayan bir değer ve bir NULL değeri her ikisi için de, yukarıda kontroller @VersionId.

Bunun yerine bir IFdeyim kullanmak ve sorguyu NULL olmayan ve diğerini NULL için arayan bir ifadeye kopyalamak performans açısından daha iyi olur mu? :

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

Veya sorgu optimizer aslında aynı yapar?

GÜNCELLEME:

(Not: SQL Server kullanıyorum)

(Ve bildiğim kadarıyla, a.VersionId = @VersionIdher iki durum için kullanmak işe yaramaz, değil mi?)



Genellikle aşağıdakileri kullanıyorum: ISNULL (a.VersionId, @VersionId) = @VersionId
628426

Yanıtlar:


36

Bu desen

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

ile değiştirilebilir

EXISTS (SELECT column INTERSECT SELECT @argument)

Bu, bir NULL ile bir NULL değerini eşleştirmenize ve motorun bir dizini columnverimli bir şekilde kullanmasına izin verecektir . Bu tekniğin mükemmel bir derinlemesine analizi için sizi Paul White'ın blog makalesine yönlendiriyorum:

Özel durumunuzda iki argüman olduğundan, aynı eşleştirme tekniğini kullanabilirsiniz @Blah- bu şekilde WHERE yan tümünün tamamını az çok özlü bir şekilde yeniden yazabilirsiniz:

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

Bu bir indeks ile hızlı bir şekilde çalışacaktır (a.Blah, a.VersionId).


Veya sorgu optimizer aslında aynı yapar?

Bu durumda, evet. SQL Server 2005'ten sonraki tüm sürümlerde (en azından), iyileştirici kalıbı tanıyabilir col = @var OR (@var IS NULL AND col IS NULL)ve uygun ISkarşılaştırmayla değiştirebilir. Bu, dahili yeniden yazma eşleşmesine dayanır, bu nedenle bunun her zaman güvenilir olmadığı daha karmaşık durumlar olabilir.

SQL Server sürümlerinde 2008 SP1 5 PB Anin gelen , ayrıca kullanma seçeneği Parametre Gömme Optimizasyonu aracılığıyla OPTION (RECOMPILE)herhangi bir parametre veya değişken çalışma zamanı değeri derleme önce bir değişmez olarak sorguda gömülü olduğu yerde.

Yani, en azından büyük ölçüde, bu durumda seçim bir stil meselesidir, ancak INTERSECTinşaat inkar edilemez derecede kompakt ve zariftir.

Aşağıdaki örnekler, her bir varyasyon için 'aynı' yürütme planını gösterir (değişmezler ve değişken referanslar hariç):

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);
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.