ステートメントを直接およびストアドプロシージャから実行する場合の異なる実行計画
-
05-07-2019 - |
質問
職場で新しいクエリを開発している間に、私はそれを書き、SQL Query Analyzerでプロファイルしました。クエリはテーブルスキャンなしで非常に優れたパフォーマンスを発揮しましたが、ストアドプロシージャにカプセル化すると、パフォーマンスはひどいものになりました。実行プランを見ると、SQL ServerがTableBでインデックスシークの代わりにテーブルスキャンを使用する別のプランを選択していることがわかりました(テーブル名と列名を少し難読化するように強制されましたが、クエリロジックはまったくありませんでした)変更されました)。
クエリは次のとおりです
SELECT
DATEADD(dd, 0, DATEDIFF(dd, 0, TableA.Created)) AS Day,
DATEPART(hh, TableA.Created) AS [Hour],
SUM(TableB.Quantity) AS Quantity,
SUM(TableB.Amount) AS Amount
FROM
TableA
INNER JOIN TableB ON TableA.BID = TableB.ID
WHERE
(TableA.ShopId = @ShopId)
GROUP BY
DATEADD(dd, 0, DATEDIFF(dd, 0, TableA.Created)),
DATEPART(hh, TableA.Created)
ORDER BY
DATEPART(hh, TableA.Created)
クエリ" raw"を実行すると、次のトレース統計を取得します
Event Class Duration CPU Reads Writes SQL:StmtCompleted 75 41 7 0
そして、次のコマンドを使用してクエリをストアドプロシージャとして実行すると
DECLARE @ShopId int
SELECT @ShopId = 1
EXEC spStats_GetSalesStatsByHour @ShopId
次のトレース統計を取得します
Event Class Duration CPU Reads Writes SQL:StmtCompleted 222 10 48 0
クエリをnvarcharに保存し、このようにsp_executesqlを使用して実行した場合も同じ結果が得られます(sprocのように動作します)
DECLARE @SQL nvarchar(2000)
SET @SQL = 'SELECT DATEADD(dd, ...'
exec sp_executesql @SQL
ストアドプロシージャには、上記のselectステートメント以外には何も含まれていません。ステートメントがストアドプロシージャとして実行されるという理由だけで、SQLサーバーが下位の実行プランを選択する原因は何ですか?
現在、 SQL Server 2000 で実行しています
解決
これは通常、パラメータスニッフィングと関係があります。対処するのは非常にイライラすることができます。ストアドプロシージャを再コンパイルすることで解決できる場合もありますし、次のようにストアドプロシージャ内で重複変数を使用することもできます:
alter procedure p_myproc (@p1 int) as
declare @p1_copy int;
set @p1_copy = @p1;
そして、クエリで@ p1_copyを使用します。ばかげているようですが、動作します。
同じトピックに関する最近の質問を確認してください:
他のヒント
はい-Oracle DB 11gでもこれを見ました-パッケージから呼び出されると、同じクエリがdbサーバーの2つのノードで高速に実行されましたが、文字通りハングアップしました!
同じ動作を得るために共有プールをクリアする必要がありました:実行プランが劣っている1つのノードのライブラリキャッシュ/メモリに古いコピーがロックされているジョブ/スクリプトが実行されていたためです。