web-dev-qa-db-ja.com

定数変数がNullの場合、実行プランが異なります

変数に値を指定するシナリオに取り組んでいます。 Null値を渡すと、クエリエンジンは結合テーブルをスキャンしませんでした。論理クエリ処理に従って、最初に[〜#〜] from [〜#〜]句が実行され、次に[ 〜#〜] on [〜#〜]および[〜#〜] join [〜#〜]が実行されます。ただし、この場合、クエリエンジンはWhere句に直接移動します。変数にNULL値がある場合の動作クエリエンジンについて説明してください。 SQL Server 2016を使用しています。

enter image description here

値が変更されると

enter image description here

4
Ashraf

矛盾検出 の例が表示されています。 SQL Serverのクエリオプティマイザーは、クエリ処理の最初にいくつかのクイックチェックを実行して、多くの(何でもない!)作業を行わずに結果を返すためのショートカットを取ることができるかどうかを判断するのに十分スマートです。このような短絡の1つは、where句に次のような「不可能な」制限が含まれているかどうかを確認することです。

WHERE 1=0

または

WHERE NULL = NULL

SQL Serverは、何かをNULLと比較していることを確認します。alwaysはfalseを返し、単に空の結果セットを返します。 WHERE句の項目がORではなくANDで区切られている場合、さらにチェックが実行され、定数スキャン計画が表示されない可能性があります。

=alwaysを使用して任意の値をNULLと比較すると、falseが返されます。代わりにWHERE @c IS NULLを使用できます。その構文を使用してクエリを書き換えると、SQL Serverが実際にクエリを「実行」することがわかります。このことを考慮:

IF OBJECT_ID(N'tempdb..#t', N'U') IS NOT NULL DROP TABLE #t;
CREATE TABLE #t 
(
    i int NOT NULL
);

DECLARE @c int = 1;

このクエリのクエリプラン:

SELECT *
FROM #t t
WHERE t.i = 1
    AND @c = NULL;

enter image description here

このクエリのクエリプランとの比較:

SELECT *
FROM #t t
WHERE t.i = 1
    AND @c IS NULL;

enter image description here

NULL値の比較に関する問題は、JOIN条件にも当てはまります。このことを考慮:

IF OBJECT_ID(N'tempdb..#t', N'U') IS NOT NULL DROP TABLE #t;
CREATE TABLE #t 
(
    i int NULL
);

IF OBJECT_ID(N'tempdb..#s', N'U') IS NOT NULL DROP TABLE #s;
CREATE TABLE #s 
(
    i int NULL
);

iの値をNULLに設定して、各テーブルに行を挿入します。

INSERT INTO #t (i) VALUES (NULL);
INSERT INTO #s (i) VALUES (NULL);

ここで、「単純な」クエリで2つのテーブルをJOINしようとすると、NULLは他のNULLとさえ比較できないため、結果は返されません。値!

SELECT *
FROM #t t
    INNER JOIN #s s ON t.i = s.i;

enter image description here

クエリプランに示されているように、両方のテーブルがクエリプロセッサによってスキャンされます。

enter image description here

つまり、NULL値の使用には注意が必要です。


今後は、コードの画像を投稿しないでください。代わりに、他の人が回答に使用できる 最小限、完全、検証可能な例 を作成してください。

8
Max Vernon