web-dev-qa-db-ja.com

インラインSQLと動的SQLで呼び出されるITVFの実行プランキャッシング

以下の2つのクエリのそれぞれが、SQL Serverで実行プランを生成する方法を誰かに説明してもらいたいと思います。私の関数_[dbo].[GetAdditionalProjectDatesForCalendar]_はITVF(インラインテーブル値関数)です。

動的SQL

_sp_executesql N'SELECT * FROM [dbo].[GetAdditionalProjectDatesForCalendar]
(@Parameter1,@Parameter2)', @Parameter1 = 'Value1', @Parameter2 = 'Value2'
_

インラインSQL

_SELECT * FROM [dbo].[GetAdditionalProjectDatesForCalendar]
('Value1','Value2')
_

バックグラウンド

動的SQLは、私が取り組んでいるC#アプリケーションから生成されたものであり、ほとんどの場合、インラインSQLと同じように機能しますが、_@Parameter1_および_@Parameter2_の特定の値ではパフォーマンスが低下します。

これが パラメータスニッフィング によってどのように発生するかを読み、動的SQLでoption(recompile)を使用しようとしましたが、パフォーマンスが完全に向上するわけではありません。

option(recompile)を使用してオンザフライで作成するのではなく、指定されたパラメーターの正確な実行プランを使用するように、C#にインラインSQLを作成させる方法を見つける必要があると感じています。また、パラメーター化を使用せずに動的SQLを作成することでこれを実行できると思いますが、SQLインジェクションのリスクが生じることはわかっています。

4
skeletank

OPTION (RECOMPILE)を使用した_sp_executesql_プランは、パラメーターの埋め込みも有効にして、各呼び出しの特定のパラメーター値に対して完全に最適化する必要があります。

質問で参照されている記事に対するItzikBen-Ganのコメントから AdventureWorks の例を取り上げます。

_CREATE FUNCTION dbo.F
(
    @NameLike nvarchar(50),
    @Sort tinyint
) RETURNS TABLE
AS
RETURN
    SELECT TOP (5)
        ProductID,
        Name
    FROM Production.Product
    WHERE
        @NameLike IS NULL
        OR Name LIKE @NameLike
    ORDER BY
        CASE WHEN @Sort = 1 THEN ProductID ELSE NULL END ASC,
        CASE WHEN @Sort = 2 THEN ProductID ELSE NULL END DESC,
        CASE WHEN @Sort = 3 THEN Name ELSE NULL END ASC,
        CASE WHEN @Sort = 4 THEN Name ELSE NULL END DESC;
_

実行計画:

_SELECT *
FROM dbo.F(N'K%', 3);
_

...インライン化されている特定のパラメーター値を示します(したがって、_@NameLike IS NULL_が最適化され、シークが有効になります)が、パラメーターの埋め込みは行われないため、_ORDER BY_句のトップNソートがあります。

inlined values only

再コンパイルクエリヒントを追加すると、パラメータの埋め込みが有効になっている(並べ替えなし)特定の値のプランが生成されます。

_SELECT *
FROM dbo.F(N'K%', 3)
OPTION (RECOMPILE);
_

inlined plus peo

クエリを_sp_executesql_でラップすると、値が明示的にパラメータ化されているため、インライン化も埋め込みも行われません。

_EXECUTE sys.sp_executesql
    N'SELECT * FROM dbo.F(@NameLike, @Sort)',
    N'@NameLike nvarchar(50), @Sort tinyint',
    @NameLike = N'K%', @Sort = 3;
_

parameterized

OPTION (RECOMPILE)を追加すると、両方の最適化が再び有効になります。

_EXECUTE sys.sp_executesql
    N'SELECT * FROM dbo.F(@NameLike, @Sort) OPTION (RECOMPILE)',
    N'@NameLike nvarchar(50), @Sort tinyint',
    @NameLike = N'K%', @Sort = 3;
_

inlined and embedded

3
Paul White 9