Моє запитання засноване на цьому: https://stackoverflow.com/q/35575990/5089204
Щоб дати відповідь там, я зробив наступний тестовий сценарій.
Тестовий сценарій
Спочатку я створюю тестову таблицю і заповнюю її 100 000 рядками. Випадкове число (0 до 1000) повинно вести до ~ 100 рядків для кожного випадкового числа. Це число вводиться у варшарський колон і як значення у ваш XML.
Тоді я виконую дзвінок, як ОП, там він потребує. Насправді я здійснюю дзвінки двічі: другий раз у зміненому порядку та з трохи зміненими парами пошуку та з "// item" замість повного шляху, щоб уникнути помилкових позитивних результатів через кешовані результати чи плани.
Потім я створюю індекс XML і роблю ті ж дзвінки
Тепер - що насправді мене здивувало! - .nodes
з повним шляхом набагато повільніше , ніж раніше (9 секунд) , але .exist()
це до половини секунди, з повним шляхом навіть приблизно до 0,10 сек. (хоча .nodes()
з коротким шляхом краще, але все ще далеко позаду .exist()
)
Запитання:
Мої власні тести підсумовуються коротко: індекси XML можуть надзвичайно підірвати базу даних. Вони можуть надзвичайно прискорити роботи (наприклад, редагувати 2), але також можуть уповільнити ваші запити. Я хотів би зрозуміти, як вони працюють ... Коли потрібно створити індекс XML? Чому .nodes()
з індексом може бути гірше, ніж без? Як можна уникнути негативного впливу?
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 - Результати
Це один результат, коли SQL Server 2012 локально встановлений на середньому ноутбуці. У цьому тесті я не зміг відтворити надзвичайно негативний вплив на NodesFullPath_with_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 Тест з більшим XML
Згідно з пропозицією TT, я використав XML вище, але скопіював item
-узли, щоб досягти близько 450 елементів. Я дозволяю, щоб вузол звернення був дуже високо в XML (тому що я думаю, що .exist()
він зупиниться при першому зверненні, тоді як .nodes()
продовжиться)
Створення XML-індексу підірвало mdf-файл до ~ 21GB, ~ 18GB, схоже, належать до індексу (!!!)
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()
та.exist()
переконливі. Також той факт, що індексfull path search
швидше, здається зрозумілим легко. Це означатиме: Якщо ви створюєте індекс XML, ви завжди повинні знати про негативний вплив будь-якого загального XPath (//
або*
або..
або[filter]
або що-небудь не просто звичайне Xpath ...). Насправді, ви повинні використовувати лише повний шлях - досить чудовий нічия ...