使用 SQL Server 将 XML 解析为列

Posted

技术标签:

【中文标题】使用 SQL Server 将 XML 解析为列【英文标题】:Parsing XML to Columns using SQL Server 【发布时间】:2013-12-11 07:25:11 【问题描述】:

的最简单方法是什么。示例:

<ns0:root xmlns:ns0="http://herp...">
    <ns1:FirstElement xmlns:ns1="http://derpyderp...">
        <ns1:FirstElementID>AAF11303</ns1:FirstElementID>
        <ns1:FirstEValue>some random text</ns1:FirstEValue>
        <ns1:SecondElement>
            <ns1:Something>Asdsad</ns1:Something>
            <ns1:Else>
                <ns1:Stuff>sdf</ns1:Stuff>
                <ns1:StuffVal>15</ns1:StuffVal>
            </ns1:Else>
            <ns1:Else>
                <ns1:Stuff>jarjar</ns1:Stuff>
                <ns1:StuffVal>16</ns1:StuffVal>
                <ns1:StuffParam>true</ns1:StuffParam>
            </ns1:Else>
        </ns1:SecondElement>
        <ns1:randValue>dosd</ns1:randValue>
    </ns1:FirstElement>
    <ns1:FirstElement>
        <ns1:FirstElementID>DDF00301</ns1:FirstElementID>
        <ns1:FirstEValue/>
        <ns1:SecondElement>
            <ns1:Else>
                <ns1:Stuff>yessir</ns1:Stuff>
                <ns1:StuffVal>0</ns1:StuffVal>
            </ns1:Else>
        </ns1:SecondElement>        
    </ns1:FirstElement>
    <!-- ... times n the first element with a variating amount of children up to 15 levels deep -->
</ns0:root>

我希望这是一个简单的列输出,即:

FIRSTELEMENTID | FIRSTEVALUE      | SOMETHING | ELSE.STUFF | ELSE.STUFFVAL | ELSE.STUFFPARAM | RANDVALUE
'AAF11303'     |'some random text'| 'Asdasd'  | 'sdf'      | 15            | NULL            | 'dosd'
'AAF11303'     |'some random text'| 'Asdasd'  | 'jarjar'   | 16            | TRUE            | 'dosd'
'DDF00301'     | NULL             | NULL      | 'yessir'   | 0             | NULL            | NULL

现在,实际的 XML 要复杂得多,到目前为止,我看到的所有示例都涉及手动解析 XML,基本上将每个子元素作为自己的 XML 传递,并在脚本的下一个循环中单独解析,等等等等等等。

有什么方法可以像这样输出值吗?手动解析几 MB 的 XML,其中元素出现的次数、出现的次数和位置有无数种变化,这需要花费一天的时间。

我一直在考虑使用 C# 进行 CLR 程序集,并将结果作为表传递给 SQL,但我想知道是否还有其他方法。

谢谢!

【问题讨论】:

【参考方案1】:

基本上,您可以使用nodes()values() 方法来解析您的xml(以及with xmlnamespaces 来处理命名空间)。像这样的:

;with xmlnamespaces('http://herp...' as ns0, 'http://derpyderp...' as ns1)
select
    T.C.value('(../../ns1:FirstElementID/text())[1]', 'nvarchar(max)') as FirstElementID,
    T.C.value('(../../ns1:FirstEValue/text())[1]', 'nvarchar(max)') as FirstEValue,
    T.C.value('(../ns1:Something/text())[1]', 'nvarchar(max)') as Something,
    T.C.value('(ns1:Stuff/text())[1]', 'nvarchar(max)') as [Else.Stuff],
    T.C.value('(ns1:StuffVal/text())[1]', 'nvarchar(max)') as [Else.StuffVal],
    T.C.value('(ns1:StuffParam/text())[1]', 'nvarchar(max)') as [Else.StuffParam],
    T.C.value('(../../ns1:randValue/text())[1]', 'nvarchar(max)') as [randValue]
from @data.nodes('ns0:root/ns1:FirstElement/ns1:SecondElement/ns1:Else') as T(C)

这为每个ns0:root/ns1:FirstElement/ns1:SecondElement/ns1:Else 创建一行,然后获取您需要的所有值(其中一些取自父节点)。请注意,如果您的 First FirstElement 不包含任何 ns1:SecondElement/ns1:Else 节点,它将不会出现在结果集中。在这种情况下,您可能希望使用这样的查询:

;with xmlnamespaces('http://herp...' as ns0, 'http://derpyderp...' as ns1)
select
    F.C.value('(ns1:FirstElementID/text())[1]', 'nvarchar(max)') as FirstElementID,
    F.C.value('(ns1:FirstEValue/text())[1]', 'nvarchar(max)') as FirstEValue,
    S.C.value('(ns1:Something/text())[1]', 'nvarchar(max)') as Something,
    E.C.value('(ns1:Stuff/text())[1]', 'nvarchar(max)') as [Else.Stuff],
    E.C.value('(ns1:StuffVal/text())[1]', 'nvarchar(max)') as [Else.StuffVal],
    E.C.value('(ns1:StuffParam/text())[1]', 'nvarchar(max)') as [Else.StuffParam],
    F.C.value('(ns1:randValue/text())[1]', 'nvarchar(max)') as [randValue]
from @data.nodes('ns0:root/ns1:FirstElement') as F(C)
    outer apply F.C.nodes('ns1:SecondElement') as S(C)
    outer apply S.C.nodes('ns1:Else') as E(C)

sql fiddle demo

【讨论】:

谢谢,看来正是我要找的。我会测试一下然后回复你。 :) 一年后我忘记了这一点,但这个回复绝对是导致正确答案的那个。我不得不将文件中的 XML 批量插入到表中,然后对 XML 进行索引,但最终我们发现并修复了旧解决方案中的很多错误,更好地理解了整个业务需求,并且比旧解决方案的性能提高了 15 倍表现。所以真的谢谢你!

以上是关于使用 SQL Server 将 XML 解析为列的主要内容,如果未能解决你的问题,请参考以下文章

SQL Server JSON 将行转置为列

Google BigQuery SQL:将多级 JSON (list +json+list+json) 解析为列

SQL 替换类型化 XML 数据

SQL Server - Pivot 将行转换为列(带有额外的行数据)

在Microsoft SQL Server 2008中,将一张表的某列字段的值转换为列名称

SQL Server 将行数据转换为列标题