使用 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 解析为列的主要内容,如果未能解决你的问题,请参考以下文章
Google BigQuery SQL:将多级 JSON (list +json+list+json) 解析为列
SQL Server - Pivot 将行转换为列(带有额外的行数据)