テンプステーブルがシークとブックマークのルックアップを使用している間に、テーブル変数がインデックススキャンの強制的なの理由

dba.stackexchange https://dba.stackexchange.com/questions/108352

質問

テーブル変数を使用するのは、オプティマイザがインデックスシークを使用してからインデックススキャンのブックマークルックアップを使用するのを防ぐのを理解しようとしています。

テーブルの入力:

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
.

このクエリプランはシークとブックマークルックアップを使用しています。

画像の説明が入力されています

最適化業者がTEMPテーブルでブックマーク検索をしているのはなぜですか?

テーブル変数は、ストアドプロシージャでユーザ定義テーブルタイプを介して来るデータを表すためにこの例で使用されます。

外部キー値が数百回発生した場合、インデックスシークが適切ではない可能性があることを実現します。その場合、スキャンはおそらくより良い選択になるでしょう。私が作成したシナリオのために、値が10の行はありませんでした。私はまだ行動が面白いと考えていて、それが理由があるかどうかを知りたいと思います。

SQL FIDDLE

OPTION (RECOMPILE)の追加は動作を変更しませんでした。 UDDTには主キーがあります。

@@VERSIONはSQL Server 2008 R2(SP2) - 10.50.4042.0(x64)(ビルド7601:Service Pack 1)(ハイパーバイザー)

役に立ちましたか?

解決

行動の理由は、リーディングカラムとしてRowKeyを持つインデックスがないため、SQL ServerがForegeykeyと一致する行数を特定できないことです(#tempテーブルの統計から統計から推測できますが、それらの統計を推測できます)。テーブル変数/ UDTTには存在しないため、100,000行の推定値があります。これは、シーク+ルックアップよりもスキャンで処理されます。 SQL Serverが1行しか実現するまでに、遅すぎます。

あなたはあなたのUDTTを異なる方法で構築することができるかもしれません。より現代的なバージョンのSQL Serverでは、テーブル変数に2次インデックスを作成できますが、この構文は2008 R2では使用できません。

BTWあなたが入れ子になったループをヒントしてビットマップ/プローブを避けようとすると、シーク動作を得ることができます。

DECLARE @Keys TABLE (RowKey INT PRIMARY KEY); -- can't hurt

INSERT @Keys (RowKey) VALUES (10);

SELECT 
     t.RowKey
    ,t.SecondColumn
FROM
    dbo.Test t 
INNER JOIN 
    @Keys k
ON
    t.ForeignKey = k.RowKey
    OPTION (LOOP JOIN);
.

i 数年前にPaul White からこのトリックを学びました。もちろん、あなたはプロダクションコードであらゆる種類の参加ヒントを入れることに注意する必要があります - 人々が基礎となるオブジェクトを変更し、その特定の種類の結合が不可能であるか、もう最適ではなくなった場合に失敗する可能性があります。

より複雑なクエリの場合、およびSQL Server 2012以降に移動すると、トレースフラグ2453 が役立ちます。しかし、その国旗はこの単純な参加に役立ちませんでした。そして同じ免責事項が適用されるだろう - これは単なる代替のことであり、一般的には一連の文書化と厳格な回帰テスト手順なしに行わないでください。

また、Service Pack 1はサポート不足ですが、 Service Pack 3 + MS15-058

ライセンス: CC-BY-SA帰属
所属していません dba.stackexchange
scroll top