6

同じクエリを 2 回使用する必要がありますが、where 句が少し異なります。同じストアド プロシージャをビット値で単純に呼び出し、IF... ELSE... ステートメントを使用して、比較するフィールドを決定するのが効率的かどうか疑問に思っていました。

または、2 つのストアド プロシージャを作成し、アプリのロジックに基づいてそれぞれを呼び出す必要がありますか?

きちんと理解するためにも、これをもっと詳しく知りたいです。このために実行計画はどのようにコンパイルされますか? 各 IF... ELSE... の各コード ブロックに 1 つずつありますか?

それとも、1 つの大きな実行計画としてコンパイルされますか?

4

2 に答える 2

3

キャッシュされている実行プランについて心配するのは正しいことです。

Martinは、プランがキャッシュされ、最初に実行されるときにロジックの特定のブランチに対して最適化されることを示す良い例を示しています。最初の実行後、別のパラメーターを使用してストアドプロシージャ(sproc)を呼び出し、実行フローが別のブランチを選択した場合でも、そのプランは再利用されます。これは非常に悪いことであり、パフォーマンスを低下させます。私はこれが何度も起こるのを見てきました、そして根本的な原因を見つけるのに時間がかかります。

この背後にある理由は「パラメータスニッフィング」と呼ばれ、調査する価値があります。

一般的に提案されている解決策(私はアドバイスしません)は、sprocをいくつかの小さなものに分割することです。sproc内でsprocを呼び出すと、その内部sprocは、渡されるパラメーターに最適化された実行プランを取得します。

正当な理由がない場合(正当な理由はモジュール性である)にsprocをいくつかの小さなものに分割することは、醜い回避策です。Martinは、スキーマに変更を加えることでステートメントを再コンパイルできることを示しています。ステートメントの最後にOPTION(RECOMPILE)を使用します。これは、すべての変数の現在の値を考慮に入れてステートメントの再コンパイルを行うようにオプティマイザーに指示します。パラメーターだけでなくローカル変数も考慮に入れられるため、良い計画と悪い計画を区別できます。

パラメータに応じて異なるwhere句を使用してクエリを作成するという質問に戻ります。次のパターンを使用します。

WHERE
(@parameter1 is null or col1 = @parameter1  ) 
AND
(@parameter2 is null or col2 = @parameter2  ) 
...
OPTION (RECOMPILE)

欠点は、このステートメントの実行プランがキャッシュされないことです(ただし、ステートメントのポイントまでのキャッシュには影響しません)。これは、コンパイル時間が必要になるため、sprocが何度も実行される場合に影響を与える可能性があります。考慮に入れます。生産品質データを使用してテストを実行すると、問題があるかどうかにかかわらず答えが得られます。

利点は、読みやすくエレガントなsprocをコーディングでき、オプティマイザーを間違った方向に設定できないことです。

覚えておくべきもう1つのオプションは、sprocレベル(ステートメントレベルではなく)レベルで実行プランのキャッシュを無効にできることです。これは、粒度が低く、さらに重要なことに、最適化時にローカル変数の値を考慮しません。

詳細については、 http://www.sommarskog.se/dyn-search-2005.htmlhttp: //sqlinthewild.co.za/index.php/2009/03/19/catch-all-queries/をご覧ください。

于 2012-05-18T11:25:04.190 に答える
1

プロシージャに渡されたパラメータの初期値を使用して、一度コンパイルされます。一部のステートメントは遅延コンパイルの対象になる場合がありますが、その場合、最終的にコンパイルされるときにパラメーター値が何であれ、ステートメントはコンパイルされます。

これは、以下を実行して実際の実行計画を見るとわかります。

CREATE TABLE T
  (
     C INT
  )

INSERT INTO T
SELECT 1 AS C
UNION ALL
SELECT TOP (1000) 2
FROM   master..spt_values
UNION ALL
SELECT TOP (1000) 3
FROM   master..spt_values

GO

CREATE PROC P @C INT
AS
    IF @C = 1
      BEGIN
          SELECT '1'
          FROM   T
          WHERE  C = @C
      END
    ELSE IF @C = 2
      BEGIN
          SELECT '2'
          FROM   T
          WHERE  C = @C
      END
    ELSE IF @C = 3
      BEGIN
          CREATE TABLE #T
            (
               X INT
            )

          INSERT INTO #T
          VALUES     (1)

          SELECT '3'
          FROM   T,
                 #T
          WHERE  C = @C
      END

GO

EXEC P 1

EXEC P 2

EXEC P 3

DROP PROC P

DROP TABLE T 

ケースを実行すると、 of に渡された初期パラメータ値に従ってステートメントがコンパイルされたため、as not2からの推定行数が表示されます。このケースを実行すると、(まだ作成されていない) 一時テーブルへの参照はステートメントが遅延コンパイルの対象になったことを意味するため、の正確な推定カウントが得られます。T11000131000

于 2012-05-18T09:08:05.823 に答える