2008-09-14 153 views
24

将XML数据转换成各种数据库列的最佳方式是什么?到目前为止,我主要是一直在使用的节点和值的函数,像这样:将XML数据转换为SQL Server数据库列的最佳方式

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数据非常慢。

+1

也许如果你量化“规模适中”和“慢”与实际数字那么人们将能够更好的建议? – 2008-09-14 09:57:55

+0

中等> 300 - 500个节点一次 – eddiegroves 2009-03-10 19:19:33

回答

46

虽然遇到了一个非常类似的问题,但遇到了一个处理7.5MB XML文件(大约10,000个节点)的查询,大概花了3.5〜4个小时,最终放弃了。但是,经过多一点研究后,我发现使用模式键入XML并创建了XML索引(我将批量插入到表中),同一查询在〜0.04ms内完成。

这对于性能改进如何?

代码来创建一个模式:

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 

代码与类型化XML列创建该表:

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] 

代码来创建索引

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) ?'. 

,如果我试图转到我曾与节点功能选择的节点之上。例如。

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) 

希望这可以帮助别人作为这几乎是我的一天。

3

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

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

要加快速度,您可以创建XML索引。您可以为设置索引值功能性能优化。你也可以使用键入的xml列,它的性能更好。

+0

埃斯波,谢谢你的纠正。我的英语非常糟糕。 – aku 2008-09-14 10:48:19

0

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

我的测试有xml,导致插入244条记录 - 所以有244个节点。

,我重写代码平均需要0.4秒运行。(10个测试运行,从0.56秒扩展到0.344秒)性能是不是主要原因代码被改写,但新的代码需要表现更好或更好。这个旧代码循环xml节点,调用一个sp在每个循环插入一次

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

切入新代码的测试显示新sp平均需要3.7秒 - 几乎慢了10倍。

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

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服务器是做一个单独的“表值函数[XMLReader的]”返回所有244行,在加入所有备份与嵌套循环(内加入)。所以在我的情况下,我从约30栏中切碎/插入,这似乎分开发生30次。

我将不得不转储此代码,我不认为任何优化都将克服这种方法本质上很慢。我将尝试使用sp_xml_preparedocument/OPENXML方法,并查看性能是否更好。如果有人遇到来自网络搜索这个问题(像我一样)我会强烈建议您使用SQL Server碎纸这种类型之前做一些性能测试

+0

这里有趣的信息,但它被埋没了。如果你还在,将其作为一个新问题发布(如果你找到了一个好的解决方案或者发现了问题,请自己回答:-) – 2011-01-12 05:41:48

+0

这本身并不是一个答案,而是对原始问题的肯定。请发表您自己的问题,并通过对原始海报问题的评论链接到您的问题。 – jpierson 2011-02-18 06:00:55

+0

@pst嗨,是的,还在。谢谢,这是我所需要的,所以我不需要重新发布它。 – DannykPowell 2011-02-21 09:41:51

0

有一个XML Bulk load COM对象(.NET Example

MSDN从:

You can insert XML data into a SQL Server database by using an INSERT statement and the OPENXML function; however, the Bulk Load utility provides better performance when you need to insert large amounts of XML data.

+3

我一直在这条路上,我不会建议它。我们最大的抱怨是XML Bulk Load在事务性上下文中没有很好的表现。我们花了太多的时间试图让这个工作,最后,这是COM的一部分,只是不值得。 – Didaxis 2012-02-17 14:39:26

0

我给大型XML集(> 500个节点)当前的解决方案是通过使用DataSet使用SQL批量复制(System.Data.SqlClient.SqlBulkCopy)到XML加载到存储器中,然后将表传递给SqlBulkCopy(定义一个XML模式帮助)。

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

基本上,我从来没有找到解决方案,我的原始问题关于这种类型的XML碎化性能慢。由于类型化的xml查询本身很慢,或者与事务和SQL Server日志有关,它可能会很慢。我猜想,键入的xml函数从来没有设计用于在非平凡节点大小上操作。

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

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

2

我不会声称这是“最好”的解决方案,但是我已经为此目的编写了一个通用的SQL CLR过程 - 它需要一个“表格式”Xml结构(例如由FOR XML RAW返回的结构)并输出结果集。它不需要定制/知道Xml中“表格”的结构,并且结果是非常快速/高效(尽管这不是设计目标)。我只在20秒内撕碎了一个25MB(无类型)的xml变量,返回了25,000行很宽的表格。

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

3

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

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()参数确实会影响性能。从'我必须写错了 - 让我阻止它',插入2K行到大约3秒钟。这比我们通过连接运行的原始插入语句快了2倍。

5

在我的情况下,我正在运行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

相关问题