2011-01-21 7 views
4

いくつかの統計結果を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:より単純化された代表的なクエリ実行プラン:

+0

パラメータスニッフィングだけストアドプロシージャには適用されません。これは、パラメータ化された実行計画がキャッシュされ、後でパラメータの異なる値を渡すクエリの他の呼び出しによって再利用されるすべてのクエリに適用されます。 リテラル値を使用すると、クエリオプティマイザは、その特定のケースで有効ですが、任意のパラメータ値に適用する必要があるプランには適していないクエリを簡略化できます(これはパラメータスニッフィングとは異なります)正確な問題を説明するための実行計画の例を提示できますか? –

+0

@Martin私は実行計画を追加しました。 –

+0

@despart - 最初のプランでハッシュ一致に入るのはどれですか? (私は下図のように「好き」から来ていますが、これが良い計画で逆になっているのかどうか疑問に思っています) –

答えて

2

実行計画の「推定行数」プロパティを見てください。遅いバージョン(パラメータ付き)では、コンパイル時に変数の実際の値が評価されないため、SQL Serverはクエリが返す行を適切に評価することができません。統計情報を使用して、フィルタとして使用しているフィールドのカーディナリティを推定し、それに従って実行計画を作成します。

このような問題への私の解決策は、あなたが望むフィルタなどなど、多くのパラメータを持つストアドプロシージャを作成していた。

CREATE PROCEDURE your_sp @filter1 INT, @filter2 VARCHAR(446) AS 
SELECT * FROM table 
WHERE field1 = @filter1 
AND field2 LIKE @filter2 

sc.CommandText = "your_sp"; 
sc.CommandType = CommandType.StoredProcedure; 

sc.SqlParameters.Add(new SqlParameter("@filter1", SqlDbType.Int, 32) { Value = 1}); 

sc.SqlParameters.Add(new SqlParameter("@filter2", SqlDbType.VarChar, 446) { Value = "whatever%"}); 

connection.Open(); 
SqlDataReader reader = command.ExecuteReader(); 
+0

これは、計画のパラメータ化された「LIKE」ブランチから出てくる基数推定値をどのように改善するのに役立ちますか? –

+0

http://msdn.microsoft.com/en-us/library/ms175933(v=SQL.90).aspxの「カーディナリティ推定のコンパイル時式評価」を参照してください。私は理論的ではなく実践的な経験から得たものです。 – olmed0

+0

あなたの提案がここにないと私はまだ分かりません。パラメータ化されたクエリとして保持されているが、ストアドプロシージャに入れば、これが問題を解決するだろうと言っていますか? –

関連する問題