Câu hỏi của tôi dựa trên điều này: https://stackoverflow.com/q/35575990/5089204
Để đưa ra câu trả lời, tôi đã thực hiện kịch bản kiểm tra sau.
Kịch bản thử nghiệm
Đầu tiên tôi tạo một bảng thử nghiệm và điền vào nó với 100.000 hàng. Một số ngẫu nhiên (0 đến 1000) sẽ dẫn đến ~ 100 hàng cho mỗi số ngẫu nhiên. Số này được đặt vào một cột varchar và như một giá trị trong XML của bạn.
Sau đó, tôi thực hiện một cuộc gọi như OP ở đó cần nó với .exist () và với .nodes () với một lợi thế nhỏ cho lần thứ hai, nhưng cả hai đều mất từ 5 đến 6 giây. Trong thực tế, tôi thực hiện các cuộc gọi hai lần: lần thứ hai theo thứ tự hoán đổi và với các thông số tìm kiếm thay đổi một chút và với "// mục" thay vì đường dẫn đầy đủ để tránh kết quả dương tính thông qua kết quả hoặc kế hoạch được lưu trong bộ nhớ cache.
Sau đó, tôi tạo một chỉ mục XML và thực hiện các cuộc gọi tương tự
Bây giờ - điều thực sự làm tôi ngạc nhiên! - sự .nodes
với đường dẫn đầy đủ là chậm hơn nhiều so với trước (9 giây) nhưng .exist()
giảm xuống còn một nửa thứ hai, với đường dẫn đầy đủ thậm chí xuống đến khoảng 0.10 giây. (trong khi .nodes()
với con đường ngắn là tốt hơn, nhưng vẫn còn xa phía sau .exist()
)
Câu hỏi:
Các thử nghiệm của riêng tôi đưa ra ngắn gọn: các chỉ mục XML có thể làm nổ tung cơ sở dữ liệu cực kỳ. Họ có thể tăng tốc mọi thứ cực kỳ (s. Chỉnh sửa 2), nhưng cũng có thể làm chậm các truy vấn của bạn. Tôi muốn hiểu cách chúng hoạt động ... Khi nào nên tạo một chỉ mục XML? Tại sao .nodes()
với một chỉ số có thể tồi tệ hơn mà không có? Làm thế nào người ta có thể tránh được tác động tiêu cực?
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 - Kết quả
Đây là một kết quả với SQL Server 2012 được cài đặt cục bộ trên máy tính xách tay trung bình Trong thử nghiệm này, tôi không thể tái tạo tác động tiêu cực cực đoan trên NodesFullPath_with_index
, mặc dù nó chậm hơn so với không có chỉ mục ...
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
Kiểm tra EDIT 2 với XML lớn hơn
Theo đề xuất của TT, tôi đã sử dụng XML ở trên, nhưng đã sao chép các item
mã để đạt khoảng 450 mục. Tôi để nút hit rất cao trong XML (vì tôi nghĩ rằng nó .exist()
sẽ dừng ở lần truy cập đầu tiên, trong khi .nodes()
sẽ tiếp tục)
Tạo chỉ mục XML đã thổi tệp mdf lên ~ 21GB, ~ 18GB dường như thuộc về chỉ mục (!!!)
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()
và.exist()
có sức thuyết phục. Ngoài ra thực tế là chỉ số vớifull path search
nhanh hơn có vẻ dễ hiểu. Điều này có nghĩa: Nếu bạn tạo một chỉ mục XML, bạn phải luôn luôn nhận thức được những ảnh hưởng tiêu cực với bất kỳ XPath generic (//
hoặc*
hoặc..
hoặc[filter]
hoặc bất cứ điều gì không chỉ đơn giản XPath ...). Trong thực tế, bạn chỉ nên sử dụng đường dẫn đầy đủ - một kết quả rút lui tuyệt vời ...