このパターン
column = @argument OR (@argument IS NULL AND column IS NULL)
に置き換えることができます
EXISTS (SELECT column INTERSECT SELECT @argument)
これにより、NULLとNULLが一致し、エンジンがインデックスをcolumn
効率的に使用できるようになります。この手法の詳細な分析については、Paul Whiteのブログ記事を参照してください。
特定のケースには2つの引数があるので、同じマッチング手法を使用できます。@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
比較に置き換えることができます。これは内部書き換え一致に依存しているため、これが常に信頼できるとは限らない、より複雑な場合があります。
2008 SP1 CU5以降のバージョンのSQL Server では、を介してパラメーター埋め込み最適化を使用するオプションもあります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);