動的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でクエリを手動で実行して再現できます。いずれにしても、クエリでパラメータを使用すると正常に動作しますが、パラメータからハードコード値に切り替えると問題が発生します。
これはストアプロシージャ内で起こっていますか? *パラメータスニッフィング* – Lamak
はい、ストアドプロシージャ内で検索する必要があります。私はパラメータスニッフィングだとは思わない。 WITH(RECOMPILE)を追加しても修正されませんでした。 –
これは、パラメータスニッフィングを防止できる唯一の方法ではありません。たとえば、あなたのspが '@ param'というパラメータを受け取った場合は、次のように使用できます:' DECLARE @ param2 SameDataTypeAsTheOriginal SET @ param2 = @param ........(残りのコードはここにあります) ' – Lamak