将XML数据分解为SQL Server数据库列的最佳方法

将XML数据分解到各种数据库列的最佳方式是什么? 到目前为止,我主要使用如下节点和值函数:

INSERT INTO some_table (column1, column2, column3) SELECT Rows.n.value('(@column1)[1]', 'varchar(20)'), Rows.n.value('(@column2)[1]', 'nvarchar(100)'), Rows.n.value('(@column3)[1]', 'int'), FROM @xml.nodes('//Rows') Rows(n) 

但是,我发现这对于即使是中等大小的xml数据也变得非常慢。

在遇到这个问题的时候,遇到了一个非常相似的问题,我一直在处理一个7.5MB的XML文件(〜大约10,000个节点),大概3.5到4个小时,然后才放弃。

然而,经过多一点研究后,我发现使用模式键入XML并创build了XML索引(我将批量插入到表中),同样的查询在〜0.04ms内完成。

那对于提高性能怎么样!

代码来创build一个模式:

 IF EXISTS ( SELECT * FROM sys.xml_schema_collections where [name] = 'MyXmlSchema') DROP XML SCHEMA COLLECTION [MyXmlSchema] GO DECLARE @MySchema XML SET @MySchema = ( SELECT * FROM OPENROWSET ( BULK 'C:\Path\To\Schema\MySchema.xsd', SINGLE_CLOB ) AS xmlData ) CREATE XML SCHEMA COLLECTION [MyXmlSchema] AS @MySchema GO 

用一个types化的XML列创build表的代码:

 CREATE TABLE [dbo].[XmlFiles] ( [Id] [uniqueidentifier] NOT NULL, -- Data from CV element [Data] xml(CONTENT dbo.[MyXmlSchema]) NOT NULL, CONSTRAINT [PK_XmlFiles] PRIMARY KEY NONCLUSTERED ( [Id] ASC )WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY] ) ON [PRIMARY] 

代码来创build索引

 CREATE PRIMARY XML INDEX PXML_Data ON [dbo].[XmlFiles] (Data) 

有几件事情要记住。 SQL Server的Schema实现不支持xsd:include。 这意味着如果你有一个引用其他模式的模式,你必须将所有这些模式复制到一个模式中并添加它。

另外我会得到一个错误:

 XQuery [dbo.XmlFiles.Data.value()]: Cannot implicitly atomize or apply 'fn:data()' to complex content elements, found type 'xs:anyType' within inferred type 'element({http://www.mynamespace.fake/schemas}:SequenceNumber,xs:anyType) ?'. 

如果我尝试在节点functionselect的节点上方导航。 例如

 SELECT ,C.value('CVElementId[1]', 'INT') AS [CVElementId] ,C.value('../SequenceNumber[1]', 'INT') AS [Level] FROM [dbo].[XmlFiles] CROSS APPLY [Data].nodes('/CVSet/Level/CVElement') AS T(C) 

发现处理这个问题的最好方法是使用OUTER APPLY实际上在XML上执行一个“外连接”。

 SELECT ,C.value('CVElementId[1]', 'INT') AS [CVElementId] ,B.value('SequenceNumber[1]', 'INT') AS [Level] FROM [dbo].[XmlFiles] CROSS APPLY [Data].nodes('/CVSet/Level') AS T(B) OUTER APPLY B.nodes ('CVElement') AS S(C) 

希望这能帮助别人,因为那几乎是我的一天。

在我的情况下我正在运行SQL 2005 SP2(9.0)。

唯一有帮助的是添加OPTION(OPTIMIZE FOR(@your_xml_var = NULL))。 说明在下面的链接。

例:

 INSERT INTO @tbl (Tbl_ID, Name, Value, ParamData) SELECT 1, tbl.cols.value('name[1]', 'nvarchar(255)'), tbl.cols.value('value[1]', 'nvarchar(255)'), tbl.cols.query('./paramdata[1]') FROM @xml.nodes('//root') as tbl(cols) OPTION ( OPTIMIZE FOR ( @xml = NULL ) ) 

https://connect.microsoft.com/SQLServer/feedback/details/562092/an-insert-statement-using-xml-nodes-is-very-very-very-slow-in-sql2008-sp1

我不确定什么是最好的方法。 我用OPENXML构造:

 INSERT INTO Test SELECT Id, Data FROM OPENXML (@XmlDocument, '/Root/blah',2) WITH (Id int '@ID', Data varchar(10) '@DATA') 

要加快速度,您可以创buildXML索引。 您可以专门为价值函数性能优化设置索引。 你也可以使用键入的xml列,它的性能更好。

我们在这里有类似的问题。 我们的DBA(SP,你是这个人)看了我的代码,对语法做了一些调整,我们得到了我们期待的速度。 这是不寻常的,因为我从XML中select的速度很快,但插入速度很慢。 所以试试这个语法:

 INSERT INTO some_table (column1, column2, column3) SELECT Rows.n.value(N'(@column1/text())[1]', 'varchar(20)'), Rows.n.value(N'(@column2/text())[1]', 'nvarchar(100)'), Rows.n.value(N'(@column3/text())[1]', 'int') FROM @xml.nodes('//Rows') Rows(n) 

所以指定text()参数确实会影响性能。 从“我必须写错了 – 让我停下来”的3K行插入2K行。 这比我们通过连接运行的原始插入语句快了2倍。

我不会声称这是“最好的”解决scheme,但是我已经为此目的编写了一个通用的SQL CLR过程 – 它需要一个“表格式”Xml结构(例如FOR XML RAW返回的结构)并输出一个结果集。

它不需要定制/了解Xml中“表格”的结构,而且结果是非常快速/高效(尽pipe这不是devise目标)。 我只是在20秒内撕碎了一个25MB(无types)的xmlvariables,返回了一个很宽的表格的25,000行。

希望这可以帮助某人: http : //architectshack.com/ClrXmlShredder.ashx

这不是一个答案,更多的是这个问题的补充 – 我刚刚遇到了同样的问题,我可以给出的评论中的EDG要求的数字。

我的testing有xml,导致244个logging被插入 – 所以244个节点。

我正在重写的代码平均需要0.4秒才能运行(10个testing运行,从.56秒扩展到.344秒)性能不是代码被重写的主要原因,但新代码也需要执行或更好。 这个旧的代码循环xml节点,调用一个sp在每个循环插入一次

新代码几乎只是一个单一的sp; 传入xml; 切碎它。

新代码切换后的testing显示,新sp平均需要3.7秒 – 几乎慢了10倍。

我的查询是在这个问题中发布的forms;

 INSERT INTO some_table (column1, column2, column3) SELECT Rows.n.value('(@column1)[1]', 'varchar(20)'), Rows.n.value('(@column2)[1]', 'nvarchar(100)'), Rows.n.value('(@column3)[1]', 'int'), FROM @xml.nodes('//Rows') Rows(n) 

执行计划似乎表明,对于每列,sql server正在做一个单独的“表值函数[XMLReader]”返回所有244行,join所有备份与嵌套循环(Inner Join)。 所以在我的情况下,我从约30列中切碎/插入,这似乎是分开发生30次。

我将不得不转储这个代码,我不认为任何优化都将克服这种方法本质上是缓慢的。 我将尝试使用sp_xml_preparedocument / OPENXML方法,并查看性能是否更好。 如果任何人遇到这个问题从networkingsearch(如我一样),我会强烈build议你做一些性能testing,然后在SQL Server中使用这种types的粉碎

有一个XML批量加载 COM对象( .NET示例 )

来自MSDN :

您可以使用INSERT语句和OPENXML函数将XML数据插入到SQL Server数据库中; 但是,当您需要插入大量XML数据时,Bulk Load实用程序可提供更好的性能。

我当前的解决scheme是使用SQL大容量复制(System.Data.SqlClient.SqlBulkCopy),通过使用DataSet将XML加载到内存中,然后将表传递给SqlBulkCopy(定义XML模式帮助)。

显然有一个缺陷,比如不必要地使用DataSet,并首先将整个文档加载到内存中。 我想在未来进一步实现我自己的IDataReader来绕过DataSet方法,但是DataSet对于这个工作来说是“足够好”的。

基本上我从来没有find一个解决scheme,我原来的问题,这种types的XML粉碎性能慢。 由于types化的xml查询本身很慢,或者与事务和SQL Server日志有关,可能会很慢。 我猜想,键入的xml函数从来没有被devise用于非平凡的节点大小。

XML批量加载:我试过这个,速度很快,但是我无法让COM DLL在64位环境下工作,我通常会尽量避免不再支持COM DLL。

sp_xml_preparedocument / OPENXML:我从来没有走过这条路,所以有兴趣看看它是如何执行的。