web-dev-qa-db-ja.com

sp_executesqlおよびVARCHARパラメータのパフォーマンスの問題

テーブル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番目のクエリのパフォーマンスを取得するにはどうすればよいですか?

6
rnofenko

あなたが実行しているのは ローカル変数 の呪いです。

つまり、 変数を宣言する をクエリで使用すると、SQLは値を嗅ぐことができません。

変数の使用方法によっては、マジックナンバーが使用される場合があります(BETWEEN, >, >= , <, <=, <>にはさまざまな推測があります)。

等式検索では、密度ベクトルが使用されます(ただし、ここでは列が一意の問題として定義されています)が、基本的に、カーディナリティの推定値は通常です。オフオフ。より多くの情報 ここ

これは高速クエリの計画です:

Nuts

パラメータ化された動的SQLの場合、値を盗聴することができ、完全に異なる計画で完全に異なる見積もりが得られます。

Nuts

すばやく簡単なオプションが必要な場合は、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を取り除くことができます。

Nuts

5
Erik Darling