4

いくつかの統計結果を Web ビューに表示する複雑なクエリを作成しています。ビューには、ユーザーの選択に応じて、いくつかの異なるフィルターを含めることができます。また、ワイルドカードを使用する可能性もあります。

このクエリは、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 Manager でこのクエリを実行したとき、パラメーターを使用すると大幅に速度が低下することに気付きました。したがって、次の 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%'

同じ問題を抱えている人の別の例を次に示します。

パラメータ化されたクエリは、パラメータ化されていないクエリと比較して非常に遅いクエリプランを生成するのはなぜですか

parameter sniffingと呼ばれるものがあり、パラメーター化されたクエリの実行が遅くなる可能性がありますが、これはストアド プロシージャではないため、私の場合は当てはまりません。

提案された解決策の 1 つは、OPTION(RECOMPILE) または OPTION(OPTIMIZE FOR) を使用することです。フィルターに含まれているかどうかに関係なく、約 10 個のオプションのパラメーターがあり、このオプションはLIKE.

だから、私は行き止まりにいると感じており、パラメーターを取り除き、コードで動的なリテラルクエリを構築することを考えています。しかし、その後、Sql インジェクションが登場します。

では、この問題を解決する方法について他に提案はありますか? または、パラメーターを安全にエスケープする方法を知っていますか?

編集: ここでは、次を使用して、1 つのパラメーターを持つクエリの実行プランを確認できますLIKE

編集:より単純化された代表的なクエリ実行計画:

4

2 に答える 2

2

実行プランの「Estimatednumberofrows」プロパティを確認してください。低速バージョン(パラメーター付き)では、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();
于 2011-01-21T16:50:53.117 に答える