Существует ли более быстрый способ извлечения данных из узлов XML в T-SQL? - PullRequest
0 голосов
/ 18 сентября 2018

В настоящее время я пытаюсь создать хранимую процедуру в T-SQL, которая принимает в качестве входных данных таблицу XML, а затем вставляет в нее данные во временную таблицу.

Используемый мной XML имеетследующий формат:

<Table>
    <row MyFirstColumn="foo" MySecondColumn="bar" ... />
</Table>

SQL, который я использую для вставки этих данных XML во временную таблицу, имеет следующий формат:

INSERT INTO
    #TempTable
SELECT
    T.c.value('@MyFirstColumn', 'varchar(50)')
   ,T.c.value('@MySecondColumn', 'varchar(50)')
   ,...
FROM
    @x.nodes('//Table/row') T(c)

Однако я делаю это сXML-таблицы, содержащие 150 столбцов и более 200 000 строк.В настоящее время выполнение этого SQL на 10000 строк занимает ~ 142 секунды, поэтому это совершенно не подходит для работы с таблицами XML, содержащими большое количество строк.

Кто-нибудь может предложить способ ускорить этот процесс?

Ответы [ 4 ]

0 голосов
/ 15 августа 2019

Мне очень понравился ответ Микаэля Эрикссона, и я проголосовал за него, но есть один аспект:

Его тест генерирует XML-документ объемом 909 КБ с 1000 строками и 150 столбцами.И sp_xml_preparedocument в его случае занимает всего 226 миллисекунд (что очень быстро), но ...

Я пытался применить его к своему XML-документу, который составляет 521 МБ.Он содержит 2045156 строк с 11 различными столбцами, все читаются как nvarchar (255)

Когда я выбрал все 11 столбцов с помощью *:

  • select * via .value () заняло 297с
  • выбор * через openxml занял в общей сложности 231 с: (sp_xml_preparedocument занял 107 с, выбор * из openxml занял 123 с)

в этом случае лучше работает openxml!

Когда я выбрал только 2 столбца:

  • выбор 2 столбцов с помощью .value () занял 57 секунд
  • выбор 2 столбцов с помощью openxml занял в общей сложности 189 секунд: (sp_xml_preparedocument -86 с, выберите * из openxml - 103 с)

.value () в этом случае работает лучше!

Таким образом, похоже, что способ быстрее зависит от размера xml,количество строк и количество столбцов, которые вы запрашиваете из xml!

0 голосов
/ 19 сентября 2018

SQL-сервер довольно быстро справляется с XML, но вы не сказали нам самое важное: откуда берется @x?

В SQL-сервере XML не сохраняется какСтрока, которую вы видите, но в виде иерархически организованного дерева в физических таблицах.Если вы получите этот XML на основе строк и назначите его переменной типа XML, движок должен будет проанализировать весь лот и перенести весь его контент во внутренние структуры.Остальное должно быть довольно быстрым.

На первый взгляд есть два места, чтобы немного его настроить:

  • FROM @x.nodes('//Table/row') T(c)
    // будетиспользуйте глубокий поиск , движок будет просматривать каждый <row>, если ниже может быть еще один <Table>.Скорее используйте FROM @x.nodes('/Table/row') T(c).

  • И используйте 'nvarchar(50)' вместо 'varchar(50)'.Внутренне XML хранит свои строки как NVARCHAR.Вы можете избежать всех этих приведений ...

Если у вас SQL-Server 2016+ и вы контролируете отправителя, вы можете попробовать JSON.Это лучше в одноразовых действиях , потому что он не будет передавать ваши данные во внутренние структуры, прежде чем он сможет работать с ним.

0 голосов
/ 20 сентября 2018

Уничтожение XML с помощью node () / value () в SQL Server приводит к проблемам с производительностью при запросе большого количества столбцов.Для каждого столбца существует одно объединение вложенных циклов с вызовом функции xml.

План запроса с 3 столбцами:

enter image description here

План запроса с 5 столбцами:

enter image description here

Только представьте, как это будет выглядеть с более чем 150 столбцами.

Другой вариант для вас - использовать OPENXML .У него нет тех же проблем со многими столбцами.

Ваш запрос будет выглядеть примерно так:

declare @H int;
declare @X xml;

exec sys.sp_xml_preparedocument @H output,
                                @X;

select C1,
       C2,
       C3
from
       openxml(@H, 'Table/row', 0)
       with (
              C1 int,
              C2 int,
              C3 int
            );

exec sys.sp_xml_removedocument @H;

Для меня использование 150 столбцов и 1000 строк заняло около 14 секунд с узлами () / значение () и 3 секунды с OPENXML.

Голосование за изменение.

Код, использованный для тестирования;

drop table T;

go

declare @C int = 150;
declare @S nvarchar(max);
declare @X xml;
declare @N int = 1000;
declare @D datetime;

set @S = 'create table T('+
stuff((
      select top(@C) ', '+N'C'+cast(row_number() over(order by 1/0) as nvarchar(3)) + N' int'
      from sys.columns
      for xml path('')
      ), 1, 2, '') + ')'

exec sp_executesql @S;

set @S = 'insert into T select top(@N) '+
stuff((
      select top(@C) ',1'
      from sys.columns as c1
      for xml path('')
      ), 1, 1, '') + ' from sys.columns as c1, sys.columns as c2';

exec sp_executesql @S, N'@N int', @N;

set @X = (
         select *
         from dbo.T
         for xml raw, root('Table')
         );

set @S = 'select '+
stuff((
      select top(@C) ', '+N'T.X.value(''@C'+cast(row_number() over(order by 1/0) as nvarchar(3)) + N''', ''int'')'
      from sys.columns
      for xml path('')
      ), 1, 2, '') + ' from @X.nodes(''Table/row'') as T(X)'

set @D = getdate();
exec sp_executesql @S, N'@X xml', @X;
select datediff(second, @D, getdate());

set @S = 'declare @H int;
exec sp_xml_preparedocument @H output, @X;

select *
from openxml(@H, ''Table/row'', 0)
  with (' +
stuff((
      select top(@C) ', C'+cast(row_number() over(order by 1/0) as nvarchar(3))+ ' int'
      from sys.columns
      for xml path('')
      ), 1, 2, '') + ');
exec sys.sp_xml_removedocument @H';

set @D = getdate();
exec sp_executesql @S, N'@X xml', @X
select datediff(second, @D, getdate());
0 голосов
/ 18 сентября 2018

Ваши параметры зависят от того, насколько вы контролируете сервер и какую подготовку вы готовы и способны сделать.

Если у вас есть возможность очистить данные перед вызовом процедуры (запуск исполняемого файла), например) ...

Вы можете десериализовать свои данные в сущность и использовать свой инструмент ORM (nHibernate, EntityFramework и т. д.) для хранения сущности.

Вы можете анализировать XML в объект, который может обрабатывать массовый импортер, сохранять его в файл и использовать функциональность массового импорта sql.https://docs.microsoft.com/en-us/sql/t-sql/statements/bulk-insert-transact-sql?view=sql-server-2017

Если вы можете использовать пользовательские функции на сервере, вы можете использовать пользовательские функции CLR для выполнения этой работы вместо запуска в отдельном исполняемом файле.https://docs.microsoft.com/en-us/sql/relational-databases/clr-integration-database-objects-user-defined-functions/clr-user-defined-functions?view=sql-server-2017

Если я что-нибудь еще подумаю, я отредактирую этот пост.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...