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

Fastest method to import large XML into SQL-Server table

我有一个非常大但不漂亮的 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 条记录,但这个示例就可以了。

我知道 'item' 中的每个 'record' 代表什么,但对我来说它足以将所有记录值导入到带有 varchar(100) 的列中。让我们这样说 table:"

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)放入数据库只需几秒钟。

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

处理 NULL 值是 XML 中的一些特别之处。

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

<a>
    <b>hi</b>
    <c></c>
    <d/>
</a>
  • <a> 是根元素。
  • <b> 是具有 text() 节点的元素。
  • <c> 是空元素
  • <d>是自闭元素
  • <e> 是 - 嗯 - 不存在...

重要提示:<c><d>是一样的,绝对没有区别!

您可以使用

查询元素
.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)')

什么可能会打破这个:如果 <record> 的内容可能是一个空字符串,你会得到一个 NULL 而不是 ''。但它应该快得多...希望,这对你没问题...

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

只是为了补充@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);