Ma question est basée sur ceci: https://stackoverflow.com/q/35575990/5089204
Pour y répondre, j'ai fait le scénario de test suivant.
Scénario de test
Je crée d'abord une table de test et la remplis de 100 000 lignes. Un nombre aléatoire (0 à 1000) devrait donner environ 100 lignes pour chaque nombre aléatoire. Ce nombre est placé dans un varchar et en tant que valeur dans votre XML.
Ensuite, je fais un appel comme l'OP, il en a besoin avec .exist () et avec .nodes () avec un léger avantage pour la seconde, mais les deux prennent entre 5 et 6 secondes. En fait, je fais les appels deux fois: une seconde fois dans l'ordre échangé et avec des paramètres de recherche légèrement modifiés et avec "// item" au lieu du chemin complet pour éviter les faux positifs via les résultats ou les plans mis en cache.
Puis je crée un index XML et fais les mêmes appels
Maintenant, qu'est-ce qui m'a vraiment surpris? - le .nodes
avec le chemin complet est beaucoup plus lente qu'auparavant (9 secs) , mais l' .exist()
est vers le bas à une demi - seconde, avec le chemin complet , même jusqu'à environ 0,10 sec. (tandis .nodes()
qu'avec court chemin c'est mieux, mais toujours loin derrière .exist()
)
Des questions:
En bref, mes propres tests: les index XML peuvent considérablement faire exploser une base de données. Ils peuvent considérablement accélérer les choses (par exemple, éditer 2), mais peuvent également ralentir vos requêtes. J'aimerais comprendre comment ils fonctionnent ... Quand faut-il créer un index XML? Pourquoi .nodes()
un index peut-il être pire que sans? Comment éviter l'impact négatif?
CREATE TABLE #testTbl(ID INT IDENTITY PRIMARY KEY, SomeData VARCHAR(100),XmlColumn XML);
GO
DECLARE @RndNumber VARCHAR(100)=(SELECT CAST(CAST(RAND()*1000 AS INT) AS VARCHAR(100)));
INSERT INTO #testTbl VALUES('Data_' + @RndNumber,
'<error application="application" host="host" type="exception" message="message" >
<serverVariables>
<item name="name1">
<value string="text" />
</item>
<item name="name2">
<value string="text2" />
</item>
<item name="name3">
<value string="text3" />
</item>
<item name="name4">
<value string="text4" />
</item>
<item name="name5">
<value string="My test ' + @RndNumber + '" />
</item>
<item name="name6">
<value string="text6" />
</item>
<item name="name7">
<value string="text7" />
</item>
</serverVariables>
</error>');
GO 100000
DECLARE @d DATETIME=GETDATE()
SELECT #testTbl.*
FROM #testTbl
CROSS APPLY XmlColumn.nodes('/error/serverVariables/item[@name="name5" and value/@string="My test 600"]') AS a(b);
SELECT CAST(GETDATE()-@d AS TIME) AS NodesFullPath_no_index;
GO
DECLARE @d DATETIME=GETDATE();
SELECT *
FROM #testTbl
WHERE XmlColumn.exist('/error/serverVariables/item[@name="name5" and value/@string="My test 600"]') = 1;
SELECT CAST(GETDATE()-@d AS TIME) AS ExistFullPath_no_index;
GO
DECLARE @d DATETIME=GETDATE();
SELECT *
FROM #testTbl
WHERE XmlColumn.exist('//item[@name="name5" and value/@string="My test 500"]') = 1;
SELECT CAST(GETDATE()-@d AS TIME) AS ExistShortPath_no_index;
GO
DECLARE @d DATETIME=GETDATE()
SELECT #testTbl.*
FROM #testTbl
CROSS APPLY XmlColumn.nodes('//item[@name="name5" and value/@string="My test 500"]') AS a(b);
SELECT CAST(GETDATE()-@d AS TIME) AS NodesShortPath_no_index;
GO
CREATE PRIMARY XML INDEX PXML_test_XmlColum1 ON #testTbl(XmlColumn);
CREATE XML INDEX IXML_test_XmlColumn2 ON #testTbl(XmlColumn) USING XML INDEX PXML_test_XmlColum1 FOR PATH;
GO
DECLARE @d DATETIME=GETDATE()
SELECT #testTbl.*
FROM #testTbl
CROSS APPLY XmlColumn.nodes('/error/serverVariables/item[@name="name5" and value/@string="My test 600"]') AS a(b);
SELECT CAST(GETDATE()-@d AS TIME) AS NodesFullPath_with_index;
GO
DECLARE @d DATETIME=GETDATE();
SELECT *
FROM #testTbl
WHERE XmlColumn.exist('/error/serverVariables/item[@name="name5" and value/@string="My test 600"]') = 1;
SELECT CAST(GETDATE()-@d AS TIME) AS ExistFullPath_with_index;
GO
DECLARE @d DATETIME=GETDATE();
SELECT *
FROM #testTbl
WHERE XmlColumn.exist('//item[@name="name5" and value/@string="My test 500"]') = 1;
SELECT CAST(GETDATE()-@d AS TIME) AS ExistShortPath_with_index;
GO
DECLARE @d DATETIME=GETDATE()
SELECT #testTbl.*
FROM #testTbl
CROSS APPLY XmlColumn.nodes('//item[@name="name5" and value/@string="My test 500"]') AS a(b);
SELECT CAST(GETDATE()-@d AS TIME) AS NodesShortPath_with_index;
GO
DROP TABLE #testTbl;
EDIT 1 - Résultats
C'est un résultat avec SQL Server 2012 installé localement sur un ordinateur portable moyen. Dans ce test, je ne pouvais pas reproduire l'impact extrêmement négatif NodesFullPath_with_index
, bien que ce soit plus lent que sans l'index ...
NodesFullPath_no_index 6.067
ExistFullPath_no_index 6.223
ExistShortPath_no_index 8.373
NodesShortPath_no_index 6.733
NodesFullPath_with_index 7.247
ExistFullPath_with_index 0.217
ExistShortPath_with_index 0.500
NodesShortPath_with_index 2.410
EDIT 2 Test avec un XML plus grand
Selon la suggestion de TT, j'ai utilisé le item
code XML ci-dessus, mais j'ai copié les nœuds pour atteindre environ 450 éléments. J'ai laissé le noeud de hit être très haut dans le XML (parce que je pense que ça .exist()
s'arrêterait au premier hit, tout en .nodes()
continuant)
La création de l'index XML a fait exploser le fichier mdf à environ 21 Go , environ 18 Go semblent appartenir à l'index (!!!)
NodesFullPath_no_index 3min44
ExistFullPath_no_index 3min39
ExistShortPath_no_index 3min49
NodesShortPath_no_index 4min00
NodesFullPath_with_index 8min20
ExistFullPath_with_index 8,5 seconds !!!
ExistShortPath_with_index 1min21
NodesShortPath_with_index 13min41 !!!
.nodes()
et.exist()
sont convaincantes. De plus, le fait que l'index avecfull path search
soit plus rapide semble facile à comprendre. Cela signifierait: Si vous créez un index XML, vous devez toujours être conscient de l'influence négative de tout XPath générique (//
ou*
ou..
ou[filter]
ou de tout ce qui n'est pas simplement Xpath ...). En fait, vous ne devriez utiliser que le chemin complet - un tirage au sort plutôt réussi ...