Мой вопрос основан на этом: https://stackoverflow.com/q/35575990/5089204
Чтобы дать ответ, я сделал следующий тест-сценарий.
Тестовый сценарий
Сначала я создаю тестовую таблицу и заполняю ее 100 000 строк. Случайное число (от 0 до 1000) должно приводить к ~ 100 строкам для каждого случайного числа. Этот номер помещается в столбец varchar и в качестве значения в ваш XML.
Затем я делаю вызов наподобие OP, где он нужен с .exist () и .nodes () с небольшим преимуществом на секунду, но оба занимают от 5 до 6 секунд. На самом деле, я делаю вызовы дважды: второй раз в порядке замены и с немного измененными параметрами поиска и с «// 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;
РЕДАКТИРОВАТЬ 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
РЕДАКТИРОВАТЬ 2 Тест с большим XML
В соответствии с предложением TT я использовал вышеупомянутый XML, но скопировал item
-nodes, чтобы получить около 450 элементов. Я позволил хит-узлу быть очень высоко в XML (потому что я думаю, что .exist()
он остановится при первом попадании, а .nodes()
будет продолжаться)
Создание XML-индекса взорвало mdf-файл до ~ 21 ГБ, ~ 18 ГБ, кажется, принадлежит к индексу (!!!)
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 ...). На самом деле, вы должны использовать только полный путь - довольно