Так что это может быть не ответ, по крайней мере, не решение, но, надеюсь, поможет понять, что происходит ...
Самая дорогая часть XML - это начальный анализ, помещенный в другиеwords: преобразование между текстовым представлением и техническим хранилищем.
Важно знать: собственный XML хранится не в виде текста, который вы видите, а в виде иерархической таблицы.Это требует очень сложной обработки, когда вы передаете текстовый XML в SQL-сервер.Вызов этого XML для читателя-человека требует обратного процесса.Хранить эту строку в строковом столбце (помните, что NTEXT
устарела на протяжении веков) быстрее, чем хранить ее как собственный XML, но вы потеряете много преимуществ.
Итак, для вашего сценария:
Я предполагаю, что вы запустили тот же скрипт, но только что изменили Order1
на Order2
.Это правильно?
Часть 1 измеряет простое SELECT
.
Чтобы обеспечить читаемое представление, SQL-сервер (или, скорее, SSMS) преобразует любое значение в какой-то текст,Если бы ваши таблицы содержали INT, GUID или DateTime, вы бы не увидели реальный битовый шаблон, не так ли?SSMS будет использовать довольно дорогие действия, чтобы создать что-то читаемое для вас.Дорогая часть - это трансформация.Для строк это не нужно, поэтому NTEXT будет работать быстрее.
Часть 2 измеряет метод .query()
(также с точки зрения «как представить результат»).
Использовали ли выCAST( AS XML)
с Order2
тоже?Тем не менее, при такой необходимости XML должен быть быстрее, потому что NTEXT придется многократно выполнять тяжелый анализ, в то время как XML уже хранится в формате с запросом ... Но ваш XQuery довольно неоптимален (из-за обратной навигации../Value
).Попробуйте это:
.query('/CustomProperty[Key[text()="AgreedToTerms"]]/Value/text()')
Это будет искать <CustomProperty>
, где есть <Key>
с заданным содержимым, и будет читать <Value>
ниже <CustomProperty>
без необходимости ../
Я бы наверняка ожидал, что XML превзойдет NTEXT с CAST здесь ... Самый первый вызов *1038* для совершенно новых таблиц и индексов может возвращать смещенные результаты ...
Часть 3 измеряет вставки
Здесь я ожидаю, что производительность будет примерно такой же ... Если вы переместите строковое значение в другой строковый столбец, это будет простое копирование.Перемещение нативного XML в другой столбец XML тоже простое копирование.
Часть 4 измеряет обновления
Это выглядит довольно странно ... Чего вы пытаетесь достичь?Код должен преобразовывать ваши собственные XML-файлы в строки и повторно анализировать их для сохранения в XML.Чтобы сделать то же самое с NTEXT, эти дорогостоящие действия вообще не потребуются ...
Некоторые общие соображения
- Если вы получили какой-то XML из за пределами , прочитайте егоиз файла, и вам нужно запросить его только один раз, строковые методы для строковых типов могут быть быстрее, но: если вы хотите хранить XML постоянно, чтобы чаще использовать и манипулировать его значениями, собственный XMLтипа будет намного лучше.
- Во многих случаях показатели производительности не измеряют то, что, по вашему мнению, вы делаете ...
- Старайтесь создавать свои тесты таким образом, чтобы представление результатов былоне является частью теста (например, выполните
INSERT
для временной таблицы, остановите часы и выдвиньте выходные данные из временной таблицы)
ОБНОВЛЕНИЕ Еще один тест для "Part 2"
Попробуйте этот тестовый скрипт:
USE master;
GO
CREATE DATABASE testShnugo;
GO
USE testShnugo;
GO
CREATE TABLE dbo.WithString(ID INT,SomeXML NTEXT);
CREATE TABLE dbo.WithXML(ID INT,SomeXML XML);
GO
--insert 100.000 rows to both tables
WITH Tally(Nmbr) AS (SELECT TOP 100000 ROW_NUMBER() OVER(ORDER BY (SELECT NULL)) FROM master..spt_values v1 CROSS JOIN master..spt_values v2)
INSERT INTO dbo.WithXML(ID,SomeXML)
SELECT Nmbr,(SELECT Nmbr AS [@nmbr],CONCAT('hallo',Nmbr) AS [SomeTest/@FindMe],CONCAT('SomeTestValue',Nmbr) As [SomeTest] FOR XML PATH('row'),ROOT('root'),TYPE)
FROM Tally
--copy everything to the second table
INSERT INTO dbo.WithString(ID,SomeXML) SELECT ID,CAST(SomeXML AS NVARCHAR(MAX)) FROM dbo.WithXML;
GO
--check the actual content
SELECT * FROM dbo.WithString;
SELECT * FROM dbo.WithXML;
GO
DECLARE @d DATETIME2=SYSUTCDATETIME();
SELECT * FROM dbo.WithString WHERE SomeXML LIKE '%FindMe="hallo333"%'
PRINT 'String-Method LIKE '
PRINT DATEDIFF(millisecond,@d,SYSUTCDATETIME());
SET @d=SYSUTCDATETIME();
SELECT * FROM dbo.WithString WHERE CAST(SomeXML AS xml).exist('/root/row[SomeTest[@FindMe="hallo333"]]')=1
PRINT 'CAST NTEXT to XML and .exist()'
PRINT DATEDIFF(millisecond,@d,SYSUTCDATETIME());
SET @d=SYSUTCDATETIME();
SELECT * FROM dbo.WithXML WHERE CAST(SomeXML AS nvarchar(MAX)) LIKE '%FindMe="hallo333"%'
PRINT 'String-Method LIKE after CAST XML to NVARCHAR(MAX)'
PRINT DATEDIFF(millisecond,@d,SYSUTCDATETIME());
SET @d=SYSUTCDATETIME();
SELECT * FROM dbo.WithXML WHERE SomeXML.exist('/root/row[SomeTest[@FindMe="hallo333"]]')=1
PRINT 'native XML with .exist()'
PRINT DATEDIFF(millisecond,@d,SYSUTCDATETIME());
GO
USE master;
GO
DROP DATABASE testShnugo;
Сначала я создаю таблицы и заполняю их 100 000 XML, как это
<root>
<row nmbr="1">
<SomeTest FindMe="hallo1">SomeTestValue1</SomeTest>
</row>
</root>
Мои результаты
String-Method LIKE
836
CAST NTEXT to XML and .exist()
1962
String-Method LIKE after CAST XML to NVARCHAR(MAX)
1079
native XML with .exist()
911
Как и ожидалосьсамый быстрый подход - строковый метод для строкового типа на очень маленьких строках .Но - конечно - это не будет таким мощным, как сложный XQuery, и не сможет справиться с пространствами имен, множественными вхождениями и т. Д.
Самым медленным является приведение NTEXT к XML с .exist()
Строковый метод для нативного XML после преобразования в строку на самом деле не так уж и плох, но это зависит от размера XML.Этот был очень крошечный ...
И 100.000 нетривиальных вызовов XQuery против 100.000 различных XML-файлов почти так же быстро, как и подход с использованием чистой строки.
ОБНОВЛЕНИЕ 2: большие XML-файлы
Я повторилпротестируйте большие XML-файлы, просто изменив приведенный выше код в одну строку
SELECT Nmbr,(SELECT TOP 100 Nmbr AS [@nmbr],CONCAT('hallo',x.Nmbr) AS [SomeTest/@FindMe],CONCAT('SomeTestValue',x.Nmbr) As [SomeTest] FROM Tally x FOR XML PATH('row'),ROOT('root'),TYPE)
Теперь каждый XML-файл будет состоять из 100 <row>
элементов.
<root>
<row nmbr="1">
<SomeTest FindMe="hallo1">SomeTestValue1</SomeTest>
</row>
<row nmbr="2">
<SomeTest FindMe="hallo2">SomeTestValue2</SomeTest>
</row>
<row nmbr="3">
<SomeTest FindMe="hallo3">SomeTestValue3</SomeTest>
</row>
...more of them
С поиском FindMe="hallo333"
это ничего не вернет, но времени найти, что возвращать нечего, нам достаточно:
String-Method LIKE
71959
CAST NTEXT to XML and .exist()
74773
String-Method LIKE after CAST XML to NVARCHAR(MAX)
104380
native XML with .exist()
16374
Самый быстрый - пока что!- теперь родной XML.Подходы к строкам теряются из-за размеров струн.
Пожалуйста, дайте мне знать ваш результат тоже.