将大型 XML 导入 SQL-Server 表的最快方法

Posted

技术标签:

【中文标题】将大型 XML 导入 SQL-Server 表的最快方法【英文标题】:Fastest method to import large XML into SQL-Server table 【发布时间】:2020-07-10 14:33:09 【问题描述】:

我有一个非常大而且不是很漂亮的 XML,我想将它导入到我的 sql-server 数据库中。 XML的格式就像我说的很丑:

<myxml xmlns="http://somenamespace.whatever.com/schemas/xmldata/1/" xmlns:xs="http://www.w3.org/2001/XMLSchema-instance">
    <mydata>
        <item>
            <record>some</record>
            <record>123</record>
            <record xs:nil="true" />
            <record>random</record>
            <record>234</record>    
        </item>
        <item>
            <record>345</record>
            <record>in all</record>
            <record>these</record>
            <record>cells</record>
            <record>123asdf</record>            
        </item>
        <item>
            <record>how</record>
            <record>to</record>
            <record>import</record>
            <record>987654321</record>
            <record xs:nil="true" />
        </item>
    </mydata>
</myxml>

这只是一个小样本。事实上,XML 超过 100ML,它有超过 200k 的项目,每个项目有 15 条记录,但这个样本就可以了。

我知道“项目”中的每个“记录”代表什么,但对我来说,它足以将所有记录值导入到带有 varchar(100) 的列中。让我们说这张表:”

CREATE TABLE [dbo].[DataFromXml](
    [Column1] [varchar](100) NULL,
    [Column2] [varchar](100) NULL,
    [Column3] [varchar](100) NULL,
    [Column4] [varchar](100) NULL,
    [Column5] [varchar](100) NULL
) ON [PRIMARY]
GO

我可以用这段代码完成这个:

CREATE TABLE XmlTable
(
    XMLData XML
)

INSERT INTO XmlTable(XMLData)
SELECT CONVERT(XML, BulkColumn) 
FROM OPENROWSET(BULK 'D:\myverylarge.xml', SINGLE_CLOB) AS x;

DECLARE @XML AS XML
SELECT @XML=XMLData FROM XmlTable

;WITH XMLNAMESPACES ('http://www.w3.org/2001/XMLSchema-instance' as xs, DEFAULT 'http://somenamespace.whatever.com/schemas/xmldata/1/')
INSERT INTO DataFromXml(Column1, Column2, Column3, Column4, Column5)
SELECT  ref.value('record[1][not(@xs:nil = "true")]' ,'varchar(100)') as Column1
        ,ref.value('record[2][not(@xs:nil = "true")]' ,'varchar(100)') as Column2
        ,ref.value('record[3][not(@xs:nil = "true")]' ,'varchar(100)') as Column3
        ,ref.value('record[4][not(@xs:nil = "true")]' ,'varchar(100)') as Column4
        ,ref.value('record[5][not(@xs:nil = "true")]' ,'varchar(100)') as Column5
        FROM @XML.nodes('/myxml/mydata/item') xmlData( ref )

这会运行一两分钟,这可能还不错。我没有很好的参考。我的感觉是,这可能会快很多,因为使用 OPENROWSET 将 XML(超过 100MB)导入数据库只需几秒钟。

我可以优化插入吗?如果可以,我该怎么做?

【问题讨论】:

“因为使用 OPENROWSET 将 XML(超过 100MB)放入数据库只需要几秒钟。” - 当然?或“在没有任何解析的情况下读取文件”需要几秒钟。您在那里进行了大量的处理,OPENROWSET 恕我直言不这样做。一点也不像。 我从来没有比较过,但你也可以试试 OpenXml()。 【参考方案1】:

处理 NULL 值是 XML 中的特殊之处。

XML 中 NULL 值的定义不存在。所以

<a>
    <b>hi</b>
    <c></c>
    <d/>
</a>
&lt;a&gt; 是根元素。 &lt;b&gt; 是一个带有 text() 节点的元素。 &lt;c&gt; 是一个空元素 &lt;d&gt; 是一个自闭合元素 &lt;e&gt; 是 - 嗯 - 不在那里......

重要提示:&lt;c&gt;&lt;d&gt;是一样的,绝对没有区别!

你可以用

查询元素
.value('(/a/b)[1]','nvarchar(100)')

您可以专门查询text()节点

.value('(/a/b/text())[1]','nvarchar(100)')

在此您可以找到一个可能的答案(有点隐藏):如果您专门查询 text() 节点,您可以在没有 NULL 检查谓词的情况下执行所有代码。

改变这个

ref.value('record[1][not(@xs:nil = "true")]' ,'varchar(100)')

到这里

ref.value('(record[1]/text())[1]' ,'varchar(100)')

可能会破坏这一点:如果&lt;record&gt; 的内容可能是一个空字符串,您将得到一个NULL,而不是''。但它应该快得多......希望,这对你来说没问题......

关于性能:阅读this 答案。它很好地涵盖了您的问题。尤其是消耗时间的部分(按照此答案中的链接进行操作)。

【讨论】:

【参考方案2】:

只是为了补充@Shnugo 的答案。

所有功劳归他所有。

这是您的确切 SQL 语句。它应该会给你大约 20% 的性能提升。请试一试。

;WITH XMLNAMESPACES ('http://www.w3.org/2001/XMLSchema-instance' as xs, DEFAULT 'http://somenamespace.whatever.com/schemas/xmldata/1/')
INSERT INTO DataFromXml(Column1, Column2, Column3, Column4, Column5)
SELECT  ref.value('(record[1]/text())[1]' ,'varchar(100)') as Column1
        ,ref.value('(record[2]/text())[1]' ,'varchar(100)') as Column2
        ,ref.value('(record[3]/text())[1]' ,'varchar(100)') as Column3
        ,ref.value('(record[4]/text())[1]' ,'varchar(100)') as Column4
        ,ref.value('(record[5]/text())[1]' ,'varchar(100)') as Column5
FROM @XML.nodes('/myxml/mydata/item') xmlData(ref);

【讨论】:

谢谢,Yitzhak,我的功劳归于你,我这边 +1 :-) 谢谢@Shnugo 和Yitzhak。我一定会试一试的。那么 INSERT INTO 怎么样,我们可以用更有效的语句替换它吗? @Stackberg,INSERT 声明没有错。

以上是关于将大型 XML 导入 SQL-Server 表的最快方法的主要内容,如果未能解决你的问题,请参考以下文章

SQL-Server中NULL值的问题

寻找一种更好的方法来使用 Laravel 处理大型 XML 文件以将部分导入 MySQL 数据库

将 XML 文件导入具有多个表的 Access DB

Sql-Server用insert插入多行数据-语法和例子

在现有的大型表上创建列存储索引的最有效方法?

用 Pandas 读取 Access 表的最简单方法是啥?