TSQL - 动态解析 XML 元数据和值
TSQL - Parse XML metadata & values dynamically
背景
我的 SQL table 中有一个 XML 列(使用 SQL 服务器)。每个节点都有不同数量的元数据。例如,在下面的示例中,第 1 步只有“否”作为元数据,而第 2 步另外还有 RBuffer。
<Step No="1" >Step Number 1</Step>
<Step No="2" RBuffer="6000">Step Number 2</Step>
<Step No="3" Macro="5">Step Number 3</Step>
预期输出
我想动态提取此元数据,同时获取值。对于上面的示例,这看起来像下面的 table。重要的是,有多少元数据标签并不重要,我希望它遍历所有这些标签。我的一些数据有 10 个以上的标签。
Node
Step
Key
Value
Step
1
Value
Step Number 1
Step
2
RBuffer
6000
Step
2
Value
Step Number 2
Step
3
Macro
5
Step
3
Value
Step Number 3
目前工作
到目前为止,我已经能够以静态方式提取元数据:
SELECT o.value('@No', 'varchar(32)') [Step]
,o.value('@Macro', 'varchar(32)') [Macro]
,o.value('@RBuffer', 'varchar(32)') [RBuffer]
,o.value('(text())[1]', 'varchar(32)') [Action]
FROM [dbo].[dw_mrd_vss_rundetail_stg] S
CROSS APPLY S.[rundata_detail].nodes('Step') xmlData(o)
给出以下 table:
Step
Macro
RBuffer
Action
1
NULL
NULL
Step Number 1
2
NULL
6000
Step Number 2
3
5
NULL
Step Number 3
但我必须显式调用每个值,并且以这种方式创建的列不可扩展。任何帮助,将不胜感激。我对 SQL 中的这种数据修改比较陌生,所以对代码的解释会很有帮助。
动态解决方案。如果“否”属性也是可选的并且节点名称也不同,
Declare @xml Xml = '<doc>
<Step No="1" >Step Number 1</Step>
<Step No="2" RBuffer="6000">Step Number 2</Step>
<Step No="3" Macro="5">Step Number 3</Step>
<Step Macro="7">Step Number 4</Step>
<Node No="5">Step Number 5</Node>
</doc>';
select x.*
from @xml.nodes('/doc/*') d(dn)
cross apply (
-- element data and "No" attr
select n.value('local-name(.)', 'varchar(32)') [node], 'Value' [Key], n.value('@No', 'varchar(32)') [Step], n.value('(text())[1]', 'varchar(32)') [Value]
from d.dn.nodes('.') s(n)
union all
-- attributes data but "No"
select n.value('local-name(../.)', 'varchar(32)') [node], n.value('local-name(.)', 'varchar(32)') [Key], n.value('../@No', 'varchar(32)') [Step], n.value ('data(.)', 'varchar(32)') [Value]
from d.dn.nodes('./@*[local-name(.)!="No"]') a(n)
) x
Returns
node Key Step Value
Step Value 1 Step Number 1
Step Value 2 Step Number 2
Step RBuffer 2 6000
Step Value 3 Step Number 3
Step Macro 3 5
Step Value Step Number 4
Step Macro 7
Node Value 5 Step Number 5
您可以 OUTER APPLY
包含属性和内部文本的序列。然后对于其中的每一个,您可以使用 local-name(.)
来获取属性的名称。
SELECT
Node = x1.step.value('local-name(.)','varchar(20)'),
Step = x1.step.value('@No','int'),
[Key] = x2.vals.value('if (local-name(.) = "") then "Value" else local-name(.)','varchar(20)'),
Value = x2.vals.value('.','nvarchar(100)')
FROM dw_mrd_vss_rundetail_stg s
CROSS APPLY s.rundata_detail.nodes('/Step') x1(step)
OUTER APPLY x1.step.nodes('(./@*[local-name(.) != "No"], ./text())') x2(vals);
如果你想包括所有节点,即使不是 Step
的节点,只需将第一个 .nodes
更改为 .nodes('/*')
背景 我的 SQL table 中有一个 XML 列(使用 SQL 服务器)。每个节点都有不同数量的元数据。例如,在下面的示例中,第 1 步只有“否”作为元数据,而第 2 步另外还有 RBuffer。
<Step No="1" >Step Number 1</Step>
<Step No="2" RBuffer="6000">Step Number 2</Step>
<Step No="3" Macro="5">Step Number 3</Step>
预期输出
我想动态提取此元数据,同时获取值。对于上面的示例,这看起来像下面的 table。重要的是,有多少元数据标签并不重要,我希望它遍历所有这些标签。我的一些数据有 10 个以上的标签。
Node | Step | Key | Value |
---|---|---|---|
Step | 1 | Value | Step Number 1 |
Step | 2 | RBuffer | 6000 |
Step | 2 | Value | Step Number 2 |
Step | 3 | Macro | 5 |
Step | 3 | Value | Step Number 3 |
目前工作
到目前为止,我已经能够以静态方式提取元数据:
SELECT o.value('@No', 'varchar(32)') [Step]
,o.value('@Macro', 'varchar(32)') [Macro]
,o.value('@RBuffer', 'varchar(32)') [RBuffer]
,o.value('(text())[1]', 'varchar(32)') [Action]
FROM [dbo].[dw_mrd_vss_rundetail_stg] S
CROSS APPLY S.[rundata_detail].nodes('Step') xmlData(o)
给出以下 table:
Step | Macro | RBuffer | Action |
---|---|---|---|
1 | NULL | NULL | Step Number 1 |
2 | NULL | 6000 | Step Number 2 |
3 | 5 | NULL | Step Number 3 |
但我必须显式调用每个值,并且以这种方式创建的列不可扩展。任何帮助,将不胜感激。我对 SQL 中的这种数据修改比较陌生,所以对代码的解释会很有帮助。
动态解决方案。如果“否”属性也是可选的并且节点名称也不同,
Declare @xml Xml = '<doc>
<Step No="1" >Step Number 1</Step>
<Step No="2" RBuffer="6000">Step Number 2</Step>
<Step No="3" Macro="5">Step Number 3</Step>
<Step Macro="7">Step Number 4</Step>
<Node No="5">Step Number 5</Node>
</doc>';
select x.*
from @xml.nodes('/doc/*') d(dn)
cross apply (
-- element data and "No" attr
select n.value('local-name(.)', 'varchar(32)') [node], 'Value' [Key], n.value('@No', 'varchar(32)') [Step], n.value('(text())[1]', 'varchar(32)') [Value]
from d.dn.nodes('.') s(n)
union all
-- attributes data but "No"
select n.value('local-name(../.)', 'varchar(32)') [node], n.value('local-name(.)', 'varchar(32)') [Key], n.value('../@No', 'varchar(32)') [Step], n.value ('data(.)', 'varchar(32)') [Value]
from d.dn.nodes('./@*[local-name(.)!="No"]') a(n)
) x
Returns
node Key Step Value
Step Value 1 Step Number 1
Step Value 2 Step Number 2
Step RBuffer 2 6000
Step Value 3 Step Number 3
Step Macro 3 5
Step Value Step Number 4
Step Macro 7
Node Value 5 Step Number 5
您可以 OUTER APPLY
包含属性和内部文本的序列。然后对于其中的每一个,您可以使用 local-name(.)
来获取属性的名称。
SELECT
Node = x1.step.value('local-name(.)','varchar(20)'),
Step = x1.step.value('@No','int'),
[Key] = x2.vals.value('if (local-name(.) = "") then "Value" else local-name(.)','varchar(20)'),
Value = x2.vals.value('.','nvarchar(100)')
FROM dw_mrd_vss_rundetail_stg s
CROSS APPLY s.rundata_detail.nodes('/Step') x1(step)
OUTER APPLY x1.step.nodes('(./@*[local-name(.) != "No"], ./text())') x2(vals);
如果你想包括所有节点,即使不是 Step
的节点,只需将第一个 .nodes
更改为 .nodes('/*')