使用 OPENXML 在 SQL 服务器中获取标签和属性的名称

Get the name of tag and attribute in SQL Server using OPENXML

我必须处理具有以下格式的 XML 文件:

<Root>
<A name="x1">
    <B exp="h1" ref="r1"/>
    <C exp="h2" ref="r2" rat = "ra1"/>
    <D exp="h3" ref="r3"/>
</A>
<A name="x2">
    <E exp="h4" ref="r4"/>
    <F exp="h5" ref="r5"/>
</A>
</Root>

我想写一个存储过程来得到一个 table 像:

|A_name|tag_name|attrbute|val|
|x1    |   B    |exp|h1|
|x1    |   B    |ref|r1|
|x1    |   C    |exp|h2|
|x1    |   C    |rat|ra1|
|x1    |   C    |ref|r2|
|x1    |   D    |exp|h3|
|x1    |   D    |ref|r3|
|x2    |   E    |exp|h4|
|x2    |   E    |ref|r4|
|x2    |   F    |exp|h5|
|x2    |   F    |ref|r5|

我该怎么办?我已经试过了

SELECT localname
FROM OPENXML(@idoc, '/Root/A') 
WHERE localname!='A'

获取标签名称。但是,我无法使用标签 A 的属性加入它们。

我发现结合使用nodes()和value()的性能比OPENXML差很多。所以即使 OPENXML 已经过时,我仍然想使用它,因为我有 GB XML 结果要读取。

如果看到 OPENXML outperform well-written nodes() and value() code when processing large amounts of data. OPENXML is an extremely RBAR 过程要求您为每个要处理的 XML 文档调用两个存储过程和一个 UDF,我会感到很惊讶。如果您使用 nodes()value() 的代码性能不佳,它可能有回溯引用(即:'../')和其他此类性能杀手。

不过,这里有一些 OPENXML 疯狂的东西会产生您正在寻找的结果集...

declare @idoc int, @doc nvarchar(max) = N'<Root>
<A name="x1">
    <B exp="h1" ref="r1"/>
    <C exp="h2" ref="r2" rat="ra1"/>
    <D exp="h3" ref="r3"/>
</A>
<A name="x2">
    <E exp="h4" ref="r4"/>
    <F exp="h5" ref="r5"/>
</A>
</Root>';

exec sp_xml_preparedocument @idoc output, @doc; 

;with rowset as (
  select id, parentid, nodetype, localname, prefix, namespaceuri, datatype, prev, [text]
  from openxml(@idoc, '/Root/A')
)
select A_name, tag_name, attribute, val
from rowset Router
outer apply (
  select A_name_id=Rinner.id
  from rowset Rinner
  where Rinner.nodetype=2 and Rinner.parentid=Router.id and Rinner.localname=N'name'
) NameAttributes
outer apply (
  select A_name=Rinner.[text]
  from rowset Rinner
  where Rinner.nodetype=3 and Rinner.parentid=NameAttributes.A_name_id and Rinner.localname=N'#text'
) NameValues
cross apply (
  select tag_id=Rinner.id, tag_name=Rinner.localname
  from rowset Rinner
  where Rinner.nodetype=1 and Rinner.parentid=Router.id
) Children
cross apply (
  select attribute_id=Rinner.id, attribute=Rinner.localname
  from rowset Rinner
  where Rinner.nodetype=2 and Rinner.parentid=Children.tag_id
) ChildAttributes
outer apply (
  select val=Rinner.[text]
  from rowset Rinner
  where Rinner.nodetype=3 and Rinner.parentid=ChildAttributes.attribute_id
) ChildAttributeValues
where nodetype=1 and parentid=0
order by Router.id, Children.tag_id, ChildAttributes.attribute

exec sp_xml_removedocument @idoc;
A_name  tag_name    attribute   val
x1      B           exp         h1
x1      B           ref         r1
x1      C           exp         h2
x1      C           rat         ra1
x1      C           ref         r2
x1      D           exp         h3
x1      D           ref         r3
x2      E           exp         h4
x2      E           ref         r4
x2      F           exp         h5
x2      F           ref         r5