Installer:
create table dbo.T
(
ID int identity primary key,
XMLDoc xml not null
);
insert into dbo.T(XMLDoc)
select (
select N.Number
for xml path(''), type
)
from (
select top(10000) row_number() over(order by (select null)) as Number
from sys.columns as c1, sys.columns as c2
) as N;
Exemple de XML pour chaque ligne:
<Number>314</Number>
Le travail de la requête consiste à compter le nombre de lignes dans T
avec une valeur spécifiée de <Number>
.
Il existe deux façons évidentes de procéder:
select count(*)
from dbo.T as T
where T.XMLDoc.value('/Number[1]', 'int') = 314;
select count(*)
from dbo.T as T
where T.XMLDoc.exist('/Number[. eq 314]') = 1;
Il se avère que value()
et exists()
nécessite deux définitions de chemin différent pour l'index XML sélectif au travail.
create selective xml index SIX_T on dbo.T(XMLDoc) for
(
pathSQL = '/Number' as sql int singleton,
pathXQUERY = '/Number' as xquery 'xs:double' singleton
);
La sql
version est pour value()
et la xquery
version est pour exist()
.
Vous pourriez penser qu'un index comme celui-ci vous donnerait un plan avec une bonne recherche mais les index XML sélectifs sont implémentés comme une table système avec la clé primaire T
comme clé principale de la clé en cluster de la table système. Les chemins spécifiés sont des colonnes éparses dans cette table. Si vous souhaitez un index des valeurs réelles des chemins définis, vous devez créer un index sélectif secondaire, un pour chaque expression de chemin.
create xml index SIX_T_pathSQL on dbo.T(XMLDoc)
using xml index SIX_T for (pathSQL);
create xml index SIX_T_pathXQUERY on dbo.T(XMLDoc)
using xml index SIX_T for (pathXQUERY);
Le plan de requête pour le exist()
fait une recherche dans l'index XML secondaire suivi d'une recherche de clé dans la table système pour l'index XML sélectif (je ne sais pas pourquoi cela est nécessaire) et enfin il fait une recherche T
pour s'assurer qu'il y en a réellement lignes là-dedans. La dernière partie est nécessaire car il n'y a pas de contrainte de clé étrangère entre la table système et T
.
Le plan de la value()
requête n'est pas si agréable. Il effectue un balayage d'index en cluster T
avec une jointure de boucles imbriquées contre une recherche sur la table interne pour obtenir la valeur de la colonne clairsemée et enfin filtre sur la valeur.
Si un indice sélectif doit être utilisé ou non est décidé avant l'optimisation, mais si un indice sélectif secondaire doit être utilisé ou non est une décision basée sur les coûts par l'optimiseur.
Pourquoi l'index sélectif secondaire n'est-il pas utilisé lorsque la clause where est filtrée value()
?
Mise à jour:
Les requêtes sont sémantiquement différentes. Si vous ajoutez une ligne avec la valeur
<Number>313</Number>
<Number>314</Number>`
la exist()
version compterait 2 lignes et la values()
requête compterait 1 ligne. Mais avec les définitions d'index telles qu'elles sont spécifiées ici, l'utilisation de la singleton
directive SQL Server vous empêchera d'ajouter une ligne avec plusieurs <Number>
éléments.
Cela ne nous permet cependant pas d'utiliser la values()
fonction sans spécifier [1]
pour garantir au compilateur que nous n'obtiendrons qu'une seule valeur. C'est [1]
la raison pour laquelle nous avons un Top N Sort dans le value()
plan.
On dirait que je me rapproche d'une réponse ici ...