いくつかの統計結果をWebビューで表示するために複雑なクエリを作成しています。ビューには、ユーザーの選択に応じていくつかの異なるフィルターを使用できます。また、ワイルドカードを使用する可能性もあります。Sql Server 2005のパラメータ化されたクエリがC#からビルドされるのを防ぐ方法
私はSqlParametersを使用してプログラムでこのクエリをC#で構築しています。クエリは次のようになります。
sc.CommandText = "SELECT * FROM table
WHERE field1 = @filter1
AND field2 LIKE @filter2"; //...and more parameters
sc.SqlParameters.Add(
new SqlParameter("@filter1", SqlDbType.Int, 32) { Value = 1});
sc.SqlParameters.Add(
new SqlParameter("@filter2", SqlDbType.VarChar, 446) { Value = "whatever%"});
これは非常に単純化されたバージョンですが、クエリ自体は重要ではありません。異なるオプションのパラメータを持つことができることを覚えておいてください(これはかなり一般的な状況です)。
Sqlマネージャでこのクエリを実行したとき、パラメータを使用するときに巨大な処理速度が低下することがわかりました。次の2つのクエリは同じでなければならず、パラメータ化されたものを実行する別の実行計画を使用しますたくさん遅く:
DECLARE @filter1 INT
DECLARE @filter2 VARCHAR 446
SET @filter1 = 1
SET @filter2 = "whatever%"
SELECT * FROM table WHERE field1 = @filter1 AND field2 LIKE @filter2
高速バージョン:ここで
SELECT * FROM table WHERE field1 = 1 AND field2 LIKE 'whatever%'
は、同じ問題を持つ人の他の例である:
Why does a parameterized query produces vastly slower query plan vs non-parameterized query
parameter sniffingという名前のものがあり、パラメータ化されたクエリの実行速度が低下する可能性がありますが、これはストアドプロシージャではないため私のケースでは適用されません。
solutionsの1つは、OPTION(RECOMPILE)またはOPTION(OPTIMIZE FOR)を使用することです。約10個のオプションのパラメータがフィルタにあるかどうかわからないので、これを行うことはできません。LIKE
を使用する場合、このオプションは機能しません。
私は死んでいると感じ、パラメータを取り除き、コード上で動的リテラルクエリを構築することを考えています。しかし、Sql Injectionがゲームに登場します。
この問題を解決する方法について他に提案がありますか?または、パラメータをエスケープする安全な方法を知っていますか?
EDIT:ここでは、1つのパラメータがLIKE
を使用して、クエリの実行計画を見ることができます:
EDIT:より単純化された代表的なクエリ実行プラン:
パラメータスニッフィングだけストアドプロシージャには適用されません。これは、パラメータ化された実行計画がキャッシュされ、後でパラメータの異なる値を渡すクエリの他の呼び出しによって再利用されるすべてのクエリに適用されます。 リテラル値を使用すると、クエリオプティマイザは、その特定のケースで有効ですが、任意のパラメータ値に適用する必要があるプランには適していないクエリを簡略化できます(これはパラメータスニッフィングとは異なります)正確な問題を説明するための実行計画の例を提示できますか? –
@Martin私は実行計画を追加しました。 –
@despart - 最初のプランでハッシュ一致に入るのはどれですか? (私は下図のように「好き」から来ていますが、これが良い計画で逆になっているのかどうか疑問に思っています) –