Найкращий спосіб написати 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 , і значення NULL для @VersionId.

Чи було б краще з точки зору продуктивності замість цього використовувати IFоператор і дублювати запит в той, який шукає не-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

Або оптимізатор запитів робить його по суті таким же?

ОНОВЛЕННЯ:

(Примітка: я використовую 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. Для відмінного глибокого аналізу цієї методики я посилаюсь на статтю в блозі Пола Уайта:

Оскільки у вашому конкретному випадку є два аргументи, ви можете використовувати ту саму техніку відповідності @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);
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.