如何获取 XML 节点的索引/位置

5 xml sql t-sql sql-server

我试图在使用 sql 选择值时获取 xml 节点的索引:

这是代码:

declare @myxml xml ='
<Departments>
    <Department>
        <Employee>
            A
        </Employee>
        <Employee>
            B
        </Employee>
    </Department>

    <Department>
        <Employee>
            C
        </Employee>
        <Employee>
            D
        </Employee>
    </Department>
</Departments>'

Select Emp = m.value('.','varchar(30)') 

from @myxml.nodes('Departments/Department/Employee') X(m)
Run Code Online (Sandbox Code Playgroud)

上述查询的输出:

Emp 
A
B
C
D
Run Code Online (Sandbox Code Playgroud)

预期输出:

Emp  Department_Index

A         1
B         1
C         2
D         2
Run Code Online (Sandbox Code Playgroud)

即我想要与部门下每个员工相对应的部门索引。这里员工A和B属于部门第​​一部门,员工C和D属于部门第​​二部门。

所以我希望这将复杂的 XML 子项与没有唯一键的父项连接起来。

Shn*_*ugo 5

这是一个解决方案,我声称它适用于所有场景 - 尽管不能保证绑定到节点的数字<Department>ROW_NUMBER()反映其在每种情况下的真实位置(请参阅下面的注释和链接):

declare @myxml xml ='
<Departments>
    <Department>
        <Employee>
            A
        </Employee>
        <Employee>
            B
        </Employee>
    </Department>

    <Department>
        <Employee>
            C
        </Employee>
        <Employee>
            D
        </Employee>
    </Department>
</Departments>';
Run Code Online (Sandbox Code Playgroud)

--查询将使用CTE将数字绑定到第一层并整体传递内部节点。最终的 SELECT 将使用传递过来的<Department>节点并选择其员工:

declare @myxml xml ='
<Departments>
    <Department>
        <Employee>
            A
        </Employee>
        <Employee>
            B
        </Employee>
    </Department>

    <Department>
        <Employee>
            C
        </Employee>
        <Employee>
            D
        </Employee>
    </Department>
</Departments>';
Run Code Online (Sandbox Code Playgroud)

如果您想了解排序顺序的保证,可以阅读此线程。特别值得一读:John Cappellettis 回答下面的聊天。在那里,我提供了一种使用 的方法XQuery,以及另一种使用计数/数字表来选择元素所在位置的方法。但这是相当复杂和缓慢的。

更新:一种保证排序顺序的方法

这种方法将即时创建一个计数表。如果你有一个数字表那就更好了......

TOP子句将将此计数限制为实际的<Department>节点数。请务必使用源表(我使用master..spt_values),该表至少具有您可能需要的行数。

第一个应用将.query()与 一起使用sql:column()以获得每个号码的正确部门节点。第二个申请将读取相关员工。

WITH Tally(Nmbr) AS
(
    SELECT TOP (SELECT @myxml.value(N'count(/Departments/Department)','int'))
           ROW_NUMBER() OVER(ORDER BY (SELECT NULL)) 
    FROM master..spt_values --just a pre-filled table with many rows
)
SELECT Nmbr
      ,e.value(N'text()[1]','nvarchar(max)') AS Employee
FROM Tally
OUTER APPLY(SELECT @myxml.query(N'/Departments/Department[sql:column("Nmbr")]')) AS A(ds)
OUTER APPLY ds.nodes(N'Department/Employee') AS B(e);
Run Code Online (Sandbox Code Playgroud)