J'ai une colonne calculée persistante sur une table qui est simplement constituée de colonnes concaténées, par exemple
CREATE TABLE dbo.T
(
ID INT IDENTITY(1, 1) NOT NULL CONSTRAINT PK_T_ID PRIMARY KEY,
A VARCHAR(20) NOT NULL,
B VARCHAR(20) NOT NULL,
C VARCHAR(20) NOT NULL,
D DATE NULL,
E VARCHAR(20) NULL,
Comp AS A + '-' + B + '-' + C PERSISTED NOT NULL
);
Dans ce Comp
n'est pas unique, et D est la date de début valide de chaque combinaison de A, B, C
, donc j'utilise la requête suivante pour obtenir la date de fin pour chacun A, B, C
(essentiellement la prochaine date de début pour la même valeur de Comp):
SELECT t1.ID,
t1.Comp,
t1.D,
D2 = ( SELECT TOP 1 t2.D
FROM dbo.T t2
WHERE t2.Comp = t1.Comp
AND t2.D > t1.D
ORDER BY t2.D
)
FROM dbo.T t1
WHERE t1.D IS NOT NULL -- DON'T CARE ABOUT INACTIVE RECORDS
ORDER BY t1.Comp;
J'ai ensuite ajouté un index à la colonne calculée pour aider à cette requête (et aussi à d'autres):
CREATE NONCLUSTERED INDEX IX_T_Comp_D ON dbo.T (Comp, D) WHERE D IS NOT NULL;
Le plan de requête m'a cependant surpris. J'aurais pensé que puisque j'ai une clause where qui l'indique D IS NOT NULL
et que je trie par Comp
, et que je ne fais référence à aucune colonne en dehors de l'index, l'index sur la colonne calculée pourrait être utilisé pour analyser t1 et t2, mais j'ai vu un index clusterisé balayage.
J'ai donc forcé l'utilisation de cet indice pour voir s'il donnait un meilleur plan:
SELECT t1.ID,
t1.Comp,
t1.D,
D2 = ( SELECT TOP 1 t2.D
FROM dbo.T t2
WHERE t2.Comp = t1.Comp
AND t2.D > t1.D
ORDER BY t2.D
)
FROM dbo.T t1 WITH (INDEX (IX_T_Comp_D))
WHERE t1.D IS NOT NULL
ORDER BY t1.Comp;
Qui a donné ce plan
Cela montre qu'une recherche de clé est utilisée, dont les détails sont les suivants:
Maintenant, selon la documentation de SQL-Server:
Vous pouvez créer un index sur une colonne calculée définie avec une expression déterministe mais imprécise si la colonne est marquée PERSISTED dans l'instruction CREATE TABLE ou ALTER TABLE. Cela signifie que le moteur de base de données stocke les valeurs calculées dans la table et les met à jour lorsque toute autre colonne dont dépend la colonne calculée est mise à jour. Le moteur de base de données utilise ces valeurs persistantes lorsqu'il crée un index sur la colonne et lorsque l'index est référencé dans une requête. Cette option vous permet de créer un index sur une colonne calculée lorsque le moteur de base de données ne peut pas prouver avec précision si une fonction qui renvoie des expressions de colonne calculées, en particulier une fonction CLR créée dans le .NET Framework, est à la fois déterministe et précise.
Donc, si, comme le disent les documents, "le moteur de base de données stocke les valeurs calculées dans la table" et que la valeur est également stockée dans mon index, pourquoi une recherche de clé est-elle requise pour obtenir A, B et C quand elles ne sont pas référencées dans la requête du tout? Je suppose qu'ils sont utilisés pour calculer Comp, mais pourquoi? De plus, pourquoi la requête peut-elle utiliser l'index sur t2
, mais pas sur t1
?
Requêtes et DDL sur SQL Fiddle
NB J'ai marqué SQL Server 2008 car c'est la version sur laquelle se trouve mon problème principal, mais j'ai également le même comportement en 2012.