テーブル変数を使用すると、オプティマイザーがインデックスシークを使用してからブックマークルックアップとインデックススキャンを使用できなくなる理由を理解しようとしています。
テーブルにデータを入力する:
CREATE TABLE dbo.Test
(
RowKey INT NOT NULL PRIMARY KEY,
SecondColumn CHAR(1) NOT NULL DEFAULT 'x',
ForeignKey INT NOT NULL
)
INSERT dbo.Test
(
RowKey,
ForeignKey
)
SELECT TOP 1000000
ROW_NUMBER() OVER (ORDER BY (SELECT 0)),
ABS(CHECKSUM(NEWID()) % 10)
FROM sys.all_objects s1
CROSS JOIN sys.all_objects s2
CREATE INDEX ix_Test_1 ON dbo.Test (ForeignKey)
テーブル変数に単一のレコードを入力し、外部キー列を検索して主キーと2番目の列を検索します。
DECLARE @Keys TABLE (RowKey INT NOT NULL)
INSERT @Keys (RowKey) VALUES (10)
SELECT
t.RowKey,
t.SecondColumn
FROM
dbo.Test t
INNER JOIN
@Keys k
ON
t.ForeignKey = k.RowKey
以下は実行計画です。
代わりに一時テーブルを使用した同じクエリ:
CREATE TABLE #Keys (RowKey INT NOT NULL)
INSERT #Keys (RowKey) VALUES (10)
SELECT
t.RowKey,
t.SecondColumn
FROM
dbo.Test t
INNER JOIN
#Keys k
ON
t.ForeignKey = k.RowKey
このクエリプランは、シークとブックマークのルックアップを使用します。
オプティマイザが、テーブル変数ではなく、一時テーブルを使用してブックマークを検索するのはなぜですか?
この例では、テーブル変数を使用して、ストアドプロシージャ内のユーザー定義のテーブルタイプから送信されるデータを表します。
外部キーの値が数十万回発生した場合、インデックスシークが適切でない可能性があることを認識しています。その場合、おそらくスキャンがより良い選択でしょう。私が作成したシナリオでは、値が10の行はありませんでした。私はまだ動作が興味深いと考えており、その理由があるかどうかを知りたいと思っています。
追加OPTION (RECOMPILE)
しても動作は変わりませんでした。UDDTには主キーがあります。
@@VERSION
SQL Server 2008 R2(SP2)-10.50.4042.0(X64)(ビルド7601:Service Pack 1)(ハイパーバイザー)