Pertanyaan saya didasarkan pada ini: https://stackoverflow.com/q/35575990/5089204
Untuk memberikan jawaban di sana saya melakukan skenario-tes berikut.
Skenario pengujian
Pertama saya membuat tabel tes dan mengisinya dengan 100.000 baris. Angka acak (0 hingga 1000) harus mengarah ke ~ 100 baris untuk setiap nomor acak. Angka ini dimasukkan ke dalam varchar col dan sebagai nilai ke dalam XML Anda.
Lalu saya melakukan panggilan seperti OP di sana membutuhkannya dengan .exist () dan dengan .nodes () dengan keuntungan kecil untuk yang kedua, tetapi keduanya membutuhkan waktu 5 hingga 6 detik. Sebenarnya saya melakukan panggilan dua kali: kedua kalinya dalam urutan bertukar dan dengan params pencarian sedikit berubah dan dengan "// item" bukannya path lengkap untuk menghindari positif palsu melalui hasil atau rencana dalam cache.
Lalu saya membuat indeks XML dan melakukan panggilan yang sama
Sekarang - apa yang benar-benar mengejutkan saya! - .nodes
dengan jalur penuh jauh lebih lambat dari sebelumnya (9 detik) tetapi .exist()
turun menjadi setengah detik, dengan jalur penuh bahkan turun menjadi sekitar 0,10 detik. (Sementara .nodes()
dengan jalur pendek lebih baik, tetapi masih jauh di belakang .exist()
)
Pertanyaan:
Tes saya sendiri memunculkan singkat: indeks XML dapat meledakkan database sangat. Mereka dapat mempercepat banyak hal (s. Edit 2), tetapi dapat memperlambat permintaan Anda juga. Saya ingin memahami cara kerjanya ... Kapan seseorang harus membuat indeks XML? Mengapa .nodes()
dengan indeks lebih buruk daripada tanpa indeks? Bagaimana orang bisa menghindari dampak negatif?
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 - Hasil
Ini adalah salah satu hasil dengan SQL Server 2012 yang diinstal secara lokal pada laptop sedang. Dalam tes ini saya tidak dapat mereproduksi dampak negatif yang ekstrem NodesFullPath_with_index
, meskipun lebih lambat daripada tanpa indeks ...
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 Uji dengan XML lebih besar
Menurut saran TT, saya menggunakan XML di atas, tetapi menyalin item
-node untuk mencapai sekitar 450 item. Saya membiarkan hit-node sangat tinggi di XML (karena saya pikir itu .exist()
akan berhenti pada hit pertama, sementara .nodes()
akan melanjutkan)
Membuat indeks-XML meledakkan file-mdf menjadi ~ 21GB, ~ 18GB tampaknya termasuk dalam indeks (!!!)
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()
dan.exist()
meyakinkan. Juga fakta bahwa indeks denganfull path search
lebih cepat tampaknya mudah dimengerti. Ini berarti: Jika Anda membuat indeks XML, Anda harus selalu menyadari pengaruh negatif dengan XPath generik (//
atau*
atau..
atau[filter]
atau apa pun tidak sekadar XPath ...). Sebenarnya Anda harus menggunakan jalur lengkap saja - undian belakang yang cukup hebat ...