有没有比这更快的方法来从T-SQL中的XML节点提取数据?

J. *_*man 4 xml sql t-sql sql-server

我目前正在尝试在T-SQL中创建一个存储过程,该存储过程将XML表作为输入,然后将数据插入到临时表中。

我正在使用的XML具有以下格式:

<Table>
    <row MyFirstColumn="foo" MySecondColumn="bar" ... />
</Table>
Run Code Online (Sandbox Code Playgroud)

我用于将此XML数据插入临时表的SQL具有以下格式:

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

但是,我使用包含150列和200,000行以上的XML表来执行此操作。目前,在10,000行上执行此SQL大约需要142秒,因此,这对于处理包含大量行的XML表是完全不合适的。

谁能建议一种加快此过程的方法?

Mik*_*son 6

查询大量列时,在SQL Server中使用node()/ value()分解XML具有性能问题。有一个嵌套循环联接,其中每一列都调用xml函数。

3列查询计划:

在此处输入图片说明

5列查询计划:

在此处输入图片说明

试想一下,超过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;
Run Code Online (Sandbox Code Playgroud)

对我而言,使用nodes()/ value()使用150列和1000行需要大约14秒,而使用OPENXML需要3秒。

投票改变。

用于测试的代码;

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());
Run Code Online (Sandbox Code Playgroud)