簡體   English   中英

有沒有比這更快的方法從T-SQL中的XML節點中提取數據?

[英]Is there a faster way than this to extract data from XML nodes in T-SQL?

我目前正在嘗試在T-SQL中創建一個存儲過程,它以XML表作為輸入,然后將數據插入到臨時表中。

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

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

我用於將此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)

但是,我使用包含150列和超過200,000行的XML表來執行此操作。 目前,在10,000行上執行此SQL需要大約142秒,因此這對於處理包含大量行的XML表是完全不合適的。

有誰能建議加快這個過程的方法?

在查詢大量列時,在SQL Server中使用nodes()/ 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;

對我來說,使用150列和1000行使用nodes()/ value()花費大約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());

您的選擇取決於您對服務器的控制程度以及您願意和能夠做的准備。

如果您能夠在調用過程之前清理數據(例如,運行可執行文件)...

您可以將數據反序列化為實體,並使用您選擇的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

如果我想到其他任何事情,我會編輯這篇文章。

SQL-Server處理XML的速度非常快,但是你並沒有告訴我們最重要的事情: @x來自哪里?

在SQL-Server中,XML不會像您看到的那樣存儲,而是存儲在物理表中的分層組織樹中。 如果您在字符串基礎上獲得此XML並將其分配給XML類型的變量,則引擎將必須解析整個批次並將其所有內容傳輸到內部結構中。 剩下的應該是相當快的。

第一眼看到有兩個地方可以調整一下:

  • FROM @x.nodes('//Table/row') T(c)
    //將使用深度搜索 ,如果下面嵌套了另一個<Table> ,引擎將查看每個<row> 而是使用FROM @x.nodes('/Table/row') T(c)

  • 並使用'nvarchar(50)'而不是'varchar(50)' 內部XML將其字符串存儲為NVARCHAR 你可以避免所有這些演員......

如果您擁有SQL-Server 2016+並且您可以控制發件人,則可以嘗試使用JSON 這在一次性操作中更好,因為在它可以使用之前,它不會在內部結構中傳輸數據。

我非常喜歡並投票支持Mikael Eriksson的回答,但有一個方面是:

他的測試生成了909 KB XML文檔,包含1000行,150列。 並且sp_xml_preparedocument在他的情況下只需要226毫秒(這真的很快),但......

我嘗試將它應用於我的XML文檔,即521 MB。 它包含2045156行,包含11個不同的列,所有行都讀取為nvarchar(255)

當我通過*選擇所有11列時:

  • select * via .value()花了297秒
  • select * via openxml共計231秒:( sp_xml_preparedocument需要107秒,select * from openxml需要123秒)

openxml在這種情況下效果更好!

當我只選擇2列時:

  • 通過.value()選擇2列需要57秒
  • 通過openxml選擇2列共計189秒:(sp_xml_preparedocument - 86秒,從openxml中選擇* - 103秒)

在這種情況下,.value()效果更好!

所以看起來哪個方法更快實際上取決於你從xml查詢的xml大小,行數和列數!

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM