Il est bien connu que SCHEMABINDING
une fonction peut éviter un spool inutile dans les plans de mise à jour:
Si vous utilisez des UDF T-SQL simples qui ne touchent à aucune table (c'est-à-dire n'accèdent pas aux données), assurez-vous de spécifier l'option
SCHEMABINDING
lors de la création des UDF. Cela rendra les UDF liés au schéma et garantira que l'optimiseur de requête ne génère aucun opérateur de spoule inutile pour les plans de requête impliquant ces UDF.
Y a-t-il d'autres avantages d'une fonction SCHEMABINDING
, même si elle n'accède pas aux données?
Oui.
Ne pas préciser WITH SCHEMABINDING
signifie que SQL Server ignore les vérifications détaillées qu'il effectue normalement sur le corps de la fonction. Il marque simplement la fonction comme l'accès aux données (comme mentionné dans le lien donné dans la question).
Il s'agit d'une optimisation des performances. S'il ne faisait pas cette hypothèse, SQL Server devrait effectuer des vérifications détaillées sur chaque appel de fonction (car la fonction non liée pouvait changer à tout moment).
Il existe cinq propriétés de fonction importantes:
Par exemple, prenez la fonction scalaire non liée suivante:
CREATE FUNCTION dbo.F
(
@i integer
)
RETURNS datetime
AS
BEGIN
RETURN '19000101';
END;
Nous pouvons examiner les cinq propriétés à l'aide d'une fonction de métadonnées:
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);
Les deux propriétés d'accès aux données ont été définies sur true et les trois autres sont définies sur false .
Cela a des implications au-delà de celles auxquelles on peut s'attendre (utilisation dans des vues indexées ou des colonnes calculées indexées, par exemple).
La propriété Déterminisme affecte en particulier l'optimiseur de requête. Il a des règles détaillées concernant les types de réécritures et de manipulations qu'il est autorisé à effectuer, et celles-ci sont très restreintes pour les éléments non déterministes. Les effets secondaires peuvent être assez subtils.
Par exemple, considérez les deux tableaux suivants:
CREATE TABLE dbo.T1
(
SomeInteger integer PRIMARY KEY
);
GO
CREATE TABLE dbo.T2
(
SomeDate datetime PRIMARY KEY
);
... et une requête qui utilise la fonction (telle que définie précédemment):
SELECT *
FROM dbo.T1 AS T1
JOIN dbo.T2 AS T2
ON T2.SomeDate = dbo.F(T1.SomeInteger);
Le plan de requête est comme prévu, avec une recherche dans le tableau T2:
Cependant, si la même requête logique est écrite à l'aide d'une table dérivée ou d'une expression de table commune:
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;
Le plan d'exécution comporte désormais une analyse, le prédicat impliquant la fonction étant coincé dans un filtre:
Cela se produit également si la table dérivée ou l'expression de table commune est remplacée par une vue ou une fonction en ligne. Un indice FORCESEEK
(et d'autres tentatives similaires) échouera:
Le problème fondamental est que l'optimiseur de requête ne peut pas réorganiser les éléments de requête non déterministes aussi librement .
Pour produire une recherche, le prédicat Filter devrait être déplacé vers le bas du plan vers l'accès aux données T2. Ce mouvement est empêché lorsque la fonction n'est pas déterministe.
Le correctif de cet exemple implique deux étapes:
WITH SCHEMABINDING
La première étape est triviale. La seconde consiste à supprimer la conversion implicite non déterministe de la chaîne en datetime
; le remplacer par un CONVERT
déterministe. Ni l'un ni l'autre ne suffit à lui seul .
ALTER FUNCTION dbo.F
(
@i integer
)
RETURNS datetime
WITH SCHEMABINDING
AS
BEGIN
-- Convert with a deterministic style
RETURN CONVERT(datetime, '19000101', 112);
END;
Les propriétés de la fonction sont désormais:
Avec l'optimiseur libéré, tous les exemples produisent maintenant le plan de recherche souhaité .
Notez que l'utilisation d'un CAST
à datetime
dans la fonction ne fonctionnerait pas, car il n'est pas possible de spécifier un style de conversion dans cette syntaxe:
ALTER FUNCTION dbo.F
(
@i integer
)
RETURNS datetime
WITH SCHEMABINDING
AS
BEGIN
-- Convert with a deterministic style
RETURN CAST('19000101' AS datetime);
END;
Cette définition de fonction produit le plan d'analyse, et les propriétés montrent qu'il reste non déterministe: