【问题标题】:Count number of level of each specific xml node in SQL Server统计 SQL Server 中每个特定 xml 节点的级别数
【发布时间】:2018-10-22 16:01:35
【问题描述】:

我正在尝试确定 SQL Server 中我的 xml 文件中所有实体标记的节点级别:

<root>
<Entities>
<Entity Name="E1">
    <Entity Name="E11">
        <Entity Name="E12">
            <Entity Name="E121"/>
            <Entity Name="E122"/>
            <Entity Name="E123"/>
        </Entity>
        <Entity Name="E13"/>
    </Entity>
</Entity>
<Entity Name="E2">
    <Entity Name="E22"/>
</Entity>
</Entities>
</root>

我需要这样返回的数据:

Name  Level
-----------
E1    1
E11   2
E12   3
E121  4
E122  4
E121  4
E13   3
E2    1
E22   2

【问题讨论】:

  • 欢迎来到stackoverflow。请花一分钟时间获取tour,尤其是How to Ask。样本数据最好以DDL + DML 的形式提供。请edit您的问题包括它,您当前的尝试和您想要的结果。更多详情,read this.

标签: sql-server xml tsql


【解决方案1】:

我还建议一种递归方法,但它是一种通用方法。这将一般地遍历 XML 逐个节点(当然不是属性 @Name,这仅对您的 XML 有效)。

注意:有些事情可能需要处理命名空间:

DECLARE @xml XML=
N'<root>
<Entities>
<Entity Name="E1">
    <Entity Name="E11">
        <Entity Name="E12">
            <Entity Name="E121"/>
            <Entity Name="E122"/>
            <Entity Name="E123"/>
        </Entity>
        <Entity Name="E13"/>
    </Entity>
</Entity>
<Entity Name="E2">
    <Entity Name="E22"/>
</Entity>
</Entities>
</root>';

WITH cte AS
(
    SELECT 1 AS Step 
          ,a.value('local-name(.)','nvarchar(max)') AS ElementPath
          ,a.value('@Name','nvarchar(max)') AS Content
          ,a.query('./*') AS TheNode
    FROM @xml.nodes('/*') A(a)
    UNION ALL
    SELECT cte.Step +1 
          ,cte.ElementPath + '/' + a.value('local-name(.)','nvarchar(max)') 
          ,a.value('@Name','nvarchar(max)')
          ,a.query('./*')
    FROM cte
    CROSS APPLY TheNode.nodes('*') A(a)
)
SELECT *
      ,TheNode.value('count(//*)','int') CountSubNodes
FROM cte
ORDER BY Content;

Step 会告诉您您正在寻找的答案。当元素是最终叶节点时,子节点的计数将为零。

结果

+------+-------------------------------------------+---------+---------------+
| Step | ElementPath                               | Content | CountSubNodes |
+------+-------------------------------------------+---------+---------------+
| 1    | root                                      | NULL    | 10            |
+------+-------------------------------------------+---------+---------------+
| 2    | root/Entities                             | NULL    | 9             |
+------+-------------------------------------------+---------+---------------+
| 3    | root/Entities/Entity                      | E1      | 6             |
+------+-------------------------------------------+---------+---------------+
| 4    | root/Entities/Entity/Entity               | E11     | 5             |
+------+-------------------------------------------+---------+---------------+
| 5    | root/Entities/Entity/Entity/Entity        | E12     | 3             |
+------+-------------------------------------------+---------+---------------+
| 6    | root/Entities/Entity/Entity/Entity/Entity | E121    | 0             |
+------+-------------------------------------------+---------+---------------+
| 6    | root/Entities/Entity/Entity/Entity/Entity | E122    | 0             |
+------+-------------------------------------------+---------+---------------+
| 6    | root/Entities/Entity/Entity/Entity/Entity | E123    | 0             |
+------+-------------------------------------------+---------+---------------+
| 5    | root/Entities/Entity/Entity/Entity        | E13     | 0             |
+------+-------------------------------------------+---------+---------------+
| 3    | root/Entities/Entity                      | E2      | 1             |
+------+-------------------------------------------+---------+---------------+
| 4    | root/Entities/Entity/Entity               | E22     | 0             |
+------+-------------------------------------------+---------+---------------+

【讨论】:

  • 谢谢!这就是我所需要的。这两个决定对我都有好处:你的决定和大卫布朗的决定。但是你的是通用的,可以与任何示例一起使用。
【解决方案2】:

您可以像这样使用递归 CTE:

declare @doc xml = '
<root>
<Entities>
<Entity Name="E1">
    <Entity Name="E11">
        <Entity Name="E12">
            <Entity Name="E121"/>
            <Entity Name="E122"/>
            <Entity Name="E123"/>
        </Entity>
        <Entity Name="E13"/>
    </Entity>
</Entity>
<Entity Name="E2">
    <Entity Name="E22"/>
</Entity>
</Entities>
</root>';

with q as
(
  select r.e.value('@Name','varchar(10)') Name, r.e.query('.') Entity, 1 depth
  from @doc.nodes('/root/Entities/Entity') r(e)
  union all
  select c.e.value('@Name','varchar(10)'), c.e.query('.') Entity, q.depth + 1 depth
  from q
  cross apply q.Entity.nodes('./Entity/Entity') c(e)
)
select Name, depth
from q
order by Name

输出

Name       depth
---------- -----------
E1         1
E11        2
E12        3
E121       4
E122       4
E123       4
E13        3
E2         1
E22        2

(9 rows affected)

【讨论】:

  • 我不认为 OP 需要这种 硬编码 到称为 `´ 的元素。我认为这个问题是一种相当通用的方法......
  • 也许吧。但这只是将一些 XPath 元素名称更改为 * 的问题。
猜你喜欢
  • 1970-01-01
  • 2023-03-22
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2022-08-15
  • 1970-01-01
相关资源
最近更新 更多