Meine Frage basiert auf diesem: https://stackoverflow.com/q/35575990/5089204
Um dort eine Antwort zu geben, habe ich folgendes Test-Szenario gemacht.
Testszenario
Zuerst erstelle ich eine Testtabelle und fülle sie mit 100.000 Zeilen. Eine Zufallszahl (0 bis 1000) sollte für jede Zufallszahl ~ 100 Zeilen ergeben. Diese Zahl wird in eine varchar-Spalte und als Wert in Ihre XML-Datei eingefügt.
Dann rufe ich wie im OP dort mit .exist () und mit .nodes () mit einem kleinen Vorteil für die Sekunde auf, aber beide brauchen 5 bis 6 Sekunden. Tatsächlich rufe ich zweimal auf: ein zweites Mal in vertauschter Reihenfolge und mit leicht geänderten Suchparametern und mit "// item" anstelle des vollständigen Pfads, um Fehlalarme über zwischengespeicherte Ergebnisse oder Pläne zu vermeiden.
Dann erstelle ich einen XML-Index und mache die gleichen Aufrufe
Nun - was hat mich wirklich überrascht! - die .nodes
mit vollem Pfad ist viel langsamer als zuvor (9 Sekunden), aber die .exist()
ist auf eine halbe Sekunde reduziert , mit vollem Pfad sogar auf ungefähr 0,10 Sekunden. (zwar .nodes()
mit kurzem weg besser, aber immer noch weit hinten .exist()
)
Fragen:
Meine eigenen Tests bringen es auf den Punkt: XML-Indizes können eine Datenbank extrem in die Luft jagen. Sie können die Dinge extrem beschleunigen (s. Bearbeiten 2), aber auch Ihre Abfragen verlangsamen. Ich würde gerne verstehen, wie sie funktionieren ... Wann sollte man einen XML-Index erstellen? Warum kann .nodes()
mit einem Index schlechter sein als ohne? Wie kann man die negativen Auswirkungen vermeiden?
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 - Ergebnisse
Dies ist ein Ergebnis NodesFullPath_with_index
, wenn SQL Server 2012 lokal auf einem mittleren Laptop installiert ist. In diesem Test konnte ich den extrem negativen Einfluss nicht reproduzieren , obwohl er langsamer ist als ohne den 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 mit größerem XML
Gemäß dem Vorschlag von TT habe ich die obige XML- item
Datei verwendet, aber die Knoten kopiert , um ungefähr 450 Elemente zu erreichen. Ich habe den Hit-Node im XML sehr hoch gesetzt (weil ich denke, dass .exist()
das beim ersten Treffer aufhören würde, während .nodes()
es weitergeht)
Das Erstellen des XML-Indexes hat die Mdf-Datei auf ~ 21GB gesprengt, ~ 18GB scheinen zum Index zu gehören (!!!)
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()
und.exist()
überzeugen. Auch die Tatsache, dass der Index mitfull path search
schneller ist, scheint leicht zu verstehen. Dies würde bedeuten: Wenn Sie einen XML-Index erstellen, müssen Sie sich immer des negativen Einflusses eines generischen XPath bewusst sein (//
oder*
oder..
oder[filter]
oder irgendetwas, das nicht nur Xpath ist ...). Tatsächlich sollten Sie nur den vollständigen Pfad verwenden - ein großartiger