Ja.
Wenn Sie nichts angeben WITH SCHEMABINDING
, überspringt SQL Server die detaillierten Prüfungen, die normalerweise für den Funktionskörper durchgeführt werden. Es markiert lediglich die Funktion als Zugriff auf Daten (wie in dem in der Frage angegebenen Link angegeben).
Dies ist eine Leistungsoptimierung. Wenn dies nicht der Fall wäre, müsste SQL Server die detaillierten Prüfungen für jeden Funktionsaufruf durchführen (da sich die ungebundene Funktion jederzeit ändern könnte).
Es gibt fünf wichtige Funktionseigenschaften:
- Determinismus
- Präzision
- Datenzugriff
- Systemdatenzugriff
- Systemüberprüfung
Nehmen Sie zum Beispiel die folgende ungebundene Skalarfunktion:
CREATE FUNCTION dbo.F
(
@i integer
)
RETURNS datetime
AS
BEGIN
RETURN '19000101';
END;
Wir können die fünf Eigenschaften mit einer Metadatenfunktion betrachten:
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);
Die beiden Datenzugriffseigenschaften wurden auf "true" und die anderen drei auf "false" gesetzt .
Dies hat Auswirkungen, die über die erwarteten hinausgehen (z. B. Verwendung in indizierten Ansichten oder indizierten berechneten Spalten).
Auswirkungen auf den Abfrageoptimierer
Die Determinism- Eigenschaft wirkt sich insbesondere auf das Abfrageoptimierungsprogramm aus. Es enthält detaillierte Regeln für die Arten von Umschreibungen und Manipulationen, die für nicht deterministische Elemente sehr eingeschränkt sind. Die Nebenwirkungen können sehr subtil sein.
Betrachten Sie beispielsweise die folgenden beiden Tabellen:
CREATE TABLE dbo.T1
(
SomeInteger integer PRIMARY KEY
);
GO
CREATE TABLE dbo.T2
(
SomeDate datetime PRIMARY KEY
);
... und eine Abfrage, die die Funktion verwendet (wie zuvor definiert):
SELECT *
FROM dbo.T1 AS T1
JOIN dbo.T2 AS T2
ON T2.SomeDate = dbo.F(T1.SomeInteger);
Der Abfrageplan ist wie erwartet und enthält eine Suche in Tabelle T2:
Wenn dieselbe logische Abfrage jedoch mithilfe einer abgeleiteten Tabelle oder eines allgemeinen Tabellenausdrucks geschrieben wird:
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;
Der Ausführungsplan enthält jetzt einen Scan, wobei das Prädikat die in einem Filter festgelegte Funktion enthält:
Dies ist auch der Fall, wenn die abgeleitete Tabelle oder der allgemeine Tabellenausdruck durch eine Ansicht oder eine Inline-Funktion ersetzt wird. Ein FORCESEEK
Hinweis (und andere ähnliche Versuche) wird nicht erfolgreich sein:
Das grundlegende Problem besteht darin, dass das Abfrageoptimierungsprogramm nicht deterministische Abfrageelemente nicht so frei neu anordnen kann .
Um eine Suche zu erzeugen, müsste das Filter-Prädikat im Plan in den T2-Datenzugriff verschoben werden. Diese Bewegung wird verhindert, wenn die Funktion nicht deterministisch ist.
Fix
Das Update für dieses Beispiel umfasst zwei Schritte:
- Hinzufügen
WITH SCHEMABINDING
- Machen Sie die Funktion deterministisch
Der erste Schritt ist trivial. Die zweite beinhaltet das Entfernen des nicht deterministischen impliziten Casts von string to datetime
; Ersetzen Sie es mit einem deterministischen CONVERT
. Beides allein reicht nicht aus .
ALTER FUNCTION dbo.F
(
@i integer
)
RETURNS datetime
WITH SCHEMABINDING
AS
BEGIN
-- Convert with a deterministic style
RETURN CONVERT(datetime, '19000101', 112);
END;
Die Funktionseigenschaften sind jetzt:
Wenn der Optimierer freigegeben ist, erzeugen alle Beispiele jetzt den gewünschten Suchplan .
Beachten Sie, dass die Verwendung eines CAST
to datetime
in der Funktion nicht funktioniert, da in dieser Syntax kein Konvertierungsstil angegeben werden kann:
ALTER FUNCTION dbo.F
(
@i integer
)
RETURNS datetime
WITH SCHEMABINDING
AS
BEGIN
-- Convert with a deterministic style
RETURN CAST('19000101' AS datetime);
END;
Diese Funktionsdefinition erzeugt den Scanplan und die Eigenschaften zeigen, dass er nicht deterministisch bleibt: