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

时间:2018-07-05 08:45:05

标签: sql sql-server xml tsql

我正在尝试使用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)

上述查询的输出:

Emp 
A
B
C
D

预期输出:

Emp  Department_Index

A         1
B         1
C         2
D         2

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

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

1 个答案:

答案 0 :(得分:0)

这是一个解决方案,我声称它可以在所有情况下都可以工作-尽管不能保证通过<Department>绑定到ROW_NUMBER()节点的数字将反映其在每个场景中的实际位置以及任何情况(请参见下面的备注和链接):

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

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

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

WITH NumberedDepartment AS
(
    SELECT ROW_NUMBER() OVER(ORDER BY (SELECT NULL)) AS DepInx
          ,d.query(N'.') AS DepartmentNode
    FROM @myxml.nodes(N'/Departments/Department') AS A(d)
)
SELECT DepInx 
      ,e.value(N'text()[1]','nvarchar(max)') AS Employee
FROM NumberedDepartment
CROSS APPLY DepartmentNode.nodes(N'Department/Employee') AS B(e);

如果您想了解有关排序顺序的保证 ,请might read this thread。特别值得一读:The chat below John Cappellettis answer。在那边,我提供了一种使用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);