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);

db<>fiddle

如果你想包括所有节点,即使不是 Step 的节点,只需将第一个 .nodes 更改为 .nodes('/*')