SCHEMABINDING
関数が更新プランで 不要なスプールを回避する できることはよく知られています。
テーブルに触れない(つまり、データにアクセスしない)単純なT-SQL UDFを使用している場合は、UDFの作成時に
SCHEMABINDING
オプションを指定してください。これにより、UDFがスキーマにバインドされ、クエリオプティマイザーがこれらのUDFを含むクエリプランに不要なスプールオペレーターを生成しないようになります。
関数がデータにアクセスしない場合でも、SCHEMABINDING
関数のその他の利点はありますか?
はい
WITH SCHEMABINDING
を指定しないと、SQL Serverは、関数本体に対して通常行われる詳細なチェックをスキップします。それは単にデータにアクセスするものとして関数をマークします(質問で与えられたリンクで言及されているように)。
これはパフォーマンスの最適化です。この仮定を行わなかった場合、SQL Serverはすべての関数呼び出しについて詳細なチェックを実行する必要があります(バインドされていない関数はいつでも変更される可能性があるため)。
fiveの重要な関数プロパティがあります:
たとえば、次のバインドされていないスカラー関数を考えます。
CREATE FUNCTION dbo.F
(
@i integer
)
RETURNS datetime
AS
BEGIN
RETURN '19000101';
END;
メタデータ関数を使用して、5つのプロパティを確認できます。
SELECT
IsDeterministic = OBJECTPROPERTYEX(Func.ID, 'IsDeterministic'),
IsPrecise = OBJECTPROPERTYEX(Func.ID, 'IsPrecise'),
IsSystemVerified = OBJECTPROPERTYEX(Func.ID, 'IsSystemVerified'),
UserDataAccess = OBJECTPROPERTYEX(Func.ID, 'UserDataAccess'),
SystemDataAccess = OBJECTPROPERTYEX(Func.ID, 'SystemDataAccess')
FROM (VALUES(OBJECT_ID(N'dbo.F', N'FN'))) AS Func (ID);
2つのデータアクセスプロパティはtrueに設定されており、他の3つはfalseに設定されています。
これは、予想されるものを超える影響があります(たとえば、インデックス付きビューやインデックス付き計算列で使用)。
Determinismプロパティは、特にクエリオプティマイザーに影響します。実行が許可されている書き換えと操作のタイプに関する詳細なルールがあり、これらは非決定的な要素に対して非常に制限されています。副作用はかなり微妙です。
たとえば、次の2つのテーブルについて考えてみます。
CREATE TABLE dbo.T1
(
SomeInteger integer PRIMARY KEY
);
GO
CREATE TABLE dbo.T2
(
SomeDate datetime PRIMARY KEY
);
...そして関数を使用するクエリ(前に定義したとおり):
SELECT *
FROM dbo.T1 AS T1
JOIN dbo.T2 AS T2
ON T2.SomeDate = dbo.F(T1.SomeInteger);
クエリプランは予想どおりで、テーブルT2へのシークが特徴です。
ただし、同じ論理クエリが派生テーブルまたは共通テーブル式を使用して記述されている場合:
WITH CTE AS
(
SELECT *, dt = dbo.F(T1.SomeInteger)
FROM dbo.T1 AS T1
)
SELECT *
FROM CTE
JOIN dbo.T2 AS T2
ON T2.SomeDate = CTE.dt;
-- Derived table
SELECT
*
FROM
(
SELECT *, dt = dbo.F(T1.SomeInteger)
FROM dbo.T1 AS T1
) AS T1
JOIN dbo.T2 AS T2
ON T2.SomeDate = T1.dt;
実行計画はスキャンを特徴としており、関数に関係する述語がフィルターでスタックしています。
これは、派生テーブルまたは共通テーブル式がビューまたはインライン関数に置き換えられた場合にも発生します。 FORCESEEK
ヒント(および他の同様の試み)は成功しません:
基本的な問題は、クエリオプティマイザーが非決定的なクエリ要素を自由に順序変更できないことです。
シークを生成するには、フィルター述語をプランの下からT2データアクセスに移動する必要があります。関数が非決定的である場合、この動きは防止されます。
この例の修正には、2つのステップが含まれます。
WITH SCHEMABINDING
を追加最初のステップは簡単です。 2番目の方法では、文字列からdatetime
への非決定的な暗黙的キャストを削除します。確定的なCONVERT
に置き換えます。 それだけでは十分ではありません。
ALTER FUNCTION dbo.F
(
@i integer
)
RETURNS datetime
WITH SCHEMABINDING
AS
BEGIN
-- Convert with a deterministic style
RETURN CONVERT(datetime, '19000101', 112);
END;
関数のプロパティは次のとおりです。
オプティマイザを解放すると、すべての例で必要なシークプランが生成されます。
関数でCAST
からdatetime
を使用しても機能しないことに注意してください。その構文では変換スタイルを指定できないためです。
ALTER FUNCTION dbo.F
(
@i integer
)
RETURNS datetime
WITH SCHEMABINDING
AS
BEGIN
-- Convert with a deterministic style
RETURN CAST('19000101' AS datetime);
END;
この関数定義はスキャン計画を生成し、プロパティはそれが非決定性のままであることを示しています。