2016-11-02 13 views
2

動的SQLとsp_executesqlを使用するようにSQL Serverストアドプロシージャをリファクタリングしました。私はすぐに劇的なパフォーマンスの低下に気付きました。これは1秒未満で復帰していた手順で、現在は4分以上消費されていました。クエリでハードコードされた値を使用すると、SQL Server 2012でインデックスが使用されない

数時間の頭痛の後、私は最終的にパラメタ付きのSQL文を実行したときにすぐに戻ってきましたが、ハードコーディングされた値で実行したときにはそれは永遠にかかりました。このクエリは、4分かかり

DECLARE @Cat VARCHAR(10); 
SET @Cat='Ginger'; 
SELECT * 
FROM MyTable a 
WHERE a.MyColumn = @Cat 

...つつ:たとえば、このクエリは1秒未満で返し

SELECT * 
FROM MyTable a 
WHERE a.MyColumn = 'Ginger' 

を...と、このクエリはまた、4分かかり:

DECLARE @SQL NVARCHAR(MAX); 
SET @SQL = N' SELECT * FROM MyTable a WHERE a.MyColumn = @Cat'; 
EXEC sp_executesql @SQL, N'@Cat VARCHAR(10)', @Cat 

(実際のクエリはより複雑です)。私はsp_executesqlが@Catパラメータを実行する前にハードコーディングされた値としてクエリに挿入し、パラメータ化されていないクエリと同じ問題を再現すると仮定しています。

高速クエリと低速クエリの実行計画の違いを見ると、高速クエリでは低速クエリで使用されないインデックスが使用されています。私はそのインデックスを使用するために遅いクエリにテーブルヒントを追加すると、それは例えば、問題を修正:動的SQLクエリにテーブルヒントを追加

SELECT * 
FROM MyTable a WITH (INDEX(IX_MyIndex_3)) 
WHERE a.MyColumn = 'Ginger' 

もそこに問題を修正します。

私の質問は二重です。最初:なぜですか? SQL Serverは、パラメータ化されたクエリを、ハードコードされた値を持つクエリとは別に扱うのはなぜですか?そして第二に、テーブルのヒントを避けるためにできることは何ですか?純粋に衛生的な理由から、テーブルヒントを使用してストアドプロシージャのコードをペーストするのは避ける方がよいでしょう。

クエリで使用されたテーブルの統計を再構築しようとしましたが、それは効果がありませんでした。

これはパラメータスニッフィングではありません。問題はストアドプロシージャ内に浮かんでいましたが、SSMSでクエリを手動で実行して再現できます。いずれにしても、クエリでパラメータを使用すると正常に動作しますが、パラメータからハードコード値に切り替えると問題が発生します。

+0

これはストアプロシージャ内で起こっていますか? *パラメータスニッフィング* – Lamak

+0

はい、ストアドプロシージャ内で検索する必要があります。私はパラメータスニッフィングだとは思わない。 WITH(RECOMPILE)を追加しても修正されませんでした。 –

+0

これは、パラメータスニッフィングを防止できる唯一の方法ではありません。たとえば、あなたのspが '@ param'というパラメータを受け取った場合は、次のように使用できます:' DECLARE @ param2 SameDataTypeAsTheOriginal SET @ param2 = @param ........(残りのコードはここにあります) ' – Lamak

答えて

0

クエリがそうのように構成されている場合:このパラメータのスニッフィングを妨げている

DECLARE @Cat VARCHAR(10); 
SET @Cat='Ginger'; 
SELECT * 
FROM MyTable a 
WHERE a.MyColumn = @Cat 

。このような状況では、SQLサーバーは、クエリの列の値の分布に従って、推測された行数に基づいて計画を作成します。これらの推測された計画は、通常、ある種の列の値の均一な分布を前提としています。あなたはオプティマイザに多くの情報を与えている

SELECT * 
FROM MyTable a WITH (INDEX(IX_MyIndex_3)) 
WHERE a.MyColumn = 'Ginger' 

、これは悪いことができます:あなたはこのようなあなたのクエリを構築

。たとえば、列に1000の行があり、10のunqiue値を持つ場合、sqlは各値が100回発生すると想定し、ハッシュ一致が結合の中で最善の演算子であると判断できます。 「生姜」を与え、統計によると「生姜」が2回発生すると、意味を持たない状況でネストされたループ演算子を使用してパフォーマンスが低下する可能性があります。あるいは、未知のシナリオでは、フィルタ演算子を使用することができ、既知のシナリオでは、一定の走査+入れ子ループを使用して行を制限することができる。これは、「ショウガ」が考慮される前の計画のオペレータにおける他の問題によって引き起こされた可能性が高い。上記のコメントに記載されているように、このような正確な値を指定すると通常は効果がありますが、クエリの他の部分に十分な情報が提供されていないと悪影響を受ける可能性があります。

関連する問題