テーブルSegments
には、DEPARTMENT(VARCHAR(10))およびBDPID(VARCHAR(10))によるインデックスがあります。
最初のクエリの実行時間は34秒です
SELECT TOP 10 c.BDPID, seg.FINAL_SEGMENT
FROM Customers c
LEFT JOIN Segments seg
ON seg.DEPARTMENT = 'DEP345'
AND seg.BDPID = c.BDPID
DEPARTMENTのパラメーターを変数に移動すると、実行時間が1秒になりました。 実行計画#2(高速)
DECLARE @dd VARCHAR(10)
SET @dd = 'DEP345'
SELECT TOP 10 c.BDPID, seg.FINAL_SEGMENT
FROM Customers c
LEFT JOIN Segments seg
ON seg.DEPARTMENT = @dd
AND seg.BDPID = c.BDPID
しかし、動的SQLを使用する必要があります。クエリをsp_execitesqlに移動すると、実行時間は再び34秒になりました。 実行計画#3(遅い)
EXECUTE sp_executesql
'SELECT TOP 10 c.BDPID, seg.FINAL_SEGMENT
FROM Customers c
LEFT JOIN Segments seg
ON seg.DEPARTMENT = @dd
AND seg.BDPID = c.BDPID',
'@dd VARCHAR(10)',
@dd = 'DEP345'
動的SQLを使用して2番目のクエリのパフォーマンスを取得するにはどうすればよいですか?
あなたが実行しているのは ローカル変数 の呪いです。
つまり、 変数を宣言する をクエリで使用すると、SQLは値を嗅ぐことができません。
変数の使用方法によっては、マジックナンバーが使用される場合があります(BETWEEN, >, >= , <, <=, <>
にはさまざまな推測があります)。
等式検索では、密度ベクトルが使用されます(ただし、ここでは列が一意の問題として定義されています)が、基本的に、カーディナリティの推定値は通常です。オフオフ。より多くの情報 ここ 。
これは高速クエリの計画です:
パラメータ化された動的SQLの場合、値を盗聴することができ、完全に異なる計画で完全に異なる見積もりが得られます。
すばやく簡単なオプションが必要な場合は、OPTIMIZE FOR UNKNOWN
を使用できます
EXECUTE sp_executesql
@stmt = N'SELECT TOP 10 c.BDPID, seg.FINAL_SEGMENT
FROM Customers c
LEFT JOIN Segments seg
ON seg.DEPARTMENT = @dd
AND seg.BDPID = c.BDPID
OPTION (OPTIMIZE FOR (@dd UNKNOWN));',
@params N'@dd VARCHAR(10)', @dd = 'DEP345'
これで密度ベクトルの推定値が得られますが、それは面倒な解決策であり、私はそれを嫌いです。もちろん、コメントからTOP 10
をクエリから削除すると、このウィンドウの外に出ます。これは、この投稿専用です。
私がむしろあなたにしてほしいのは、インデックスのチューニングです。 Segment
テーブルがクラスター化インデックスを求めて泣いているのは間違いありません-HEAPの多くの行は通常問題の兆候です( 転送されたフェッチ は1日を台無しにする可能性があります)。
ここで使用される非クラスター化インデックスはカバーしていないため、このクエリもかなり下にドラッグされています。
インクルード列としてRID Lookup
を追加すると、FINAL_SEGMENT
を取り除くことができます。