19

如何在 XML 文档中找到节点的顺序?

我拥有的是这样的文件:

<value code="1">
    <value code="11">
        <value code="111"/>
    </value>
    <value code="12">
        <value code="121">
            <value code="1211"/>
            <value code="1212"/>
        </value>
    </value>
</value>

我正试图把这个东西放到一个定义如下的表中

CREATE TABLE values(
    code int,
    parent_code int,
    ord int
)

保留 XML 文档中值的顺序(它们不能按其代码排序)。我想能够说

SELECT code 
FROM values 
WHERE parent_code = 121 
ORDER BY ord

并且结果应该确定地是

code
1211
1212

我努力了

SELECT 
    value.value('@code', 'varchar(20)') code, 
    value.value('../@code', 'varchar(20)') parent, 
    value.value('position()', 'int')
FROM @xml.nodes('/root//value') n(value)
ORDER BY code desc

但它不接受该position()函数(' position()' 只能在谓词或 XPath 选择器中使用)。

我想这是可能的,但如何?

4

6 回答 6

40

position()您可以通过计算每个节点之前的兄弟节点数来模拟该函数:

SELECT
    code = value.value('@code', 'int'),
    parent_code = value.value('../@code', 'int'),
    ord = value.value('for $i in . return count(../*[. << $i]) + 1', 'int')
FROM @Xml.nodes('//value') AS T(value)

这是结果集:

code   parent_code  ord
----   -----------  ---
1      NULL         1
11     1            1
111    11           1
12     1            2
121    12           1
1211   121          1
1212   121          2

这个怎么运作:

  • for $i in .子句定义了一个名为的变量,该变量$i包含当前节点 ( .)。这基本上是一种解决 XQuery 缺乏类似 XSLT 的current()功能的技巧。
  • ../*表达式选择当前节点的所有兄弟节点(父节点的子节点)。
  • 谓词将同级列表过滤到当前节点 ( )[. << $i]之前 ( ) 的同级列表。<<$i
  • 我们count()将前面的兄弟姐妹的数量加 1 得到位置。这样,第一个节点(没有前面的兄弟节点)被分配一个位置 1。
于 2012-03-25T19:12:25.383 回答
6

您可以获取函数返回的 xml 的位置,x.nodes()如下所示:

row_number() over (order by (select 0))

例如:

DECLARE @x XML
SET @x = '<a><b><c>abc1</c><c>def1</c></b><b><c>abc2</c><c>def2</c></b></a>'

SELECT
    b.query('.'),
    row_number() over (partition by 0 order by (select 0))
FROM
    @x.nodes('/a/b') x(b)
于 2014-02-14T10:38:35.837 回答
5

SQL Serverrow_number()实际上接受一个 xml-nodes 列来进行排序。结合递归 CTE,您可以执行以下操作:

declare @Xml xml = 
'<value code="1">
    <value code="11">
        <value code="111"/>
    </value>
    <value code="12">
        <value code="121">
            <value code="1211"/>
            <value code="1212"/>
        </value>
    </value>
</value>'

;with recur as (
    select
        ordr        = row_number() over(order by x.ml),
        parent_code = cast('' as varchar(255)),
        code        = x.ml.value('@code', 'varchar(255)'),
        children    = x.ml.query('./value')
    from @Xml.nodes('value') x(ml)
    union all
    select
        ordr        = row_number() over(order by x.ml),
        parent_code = recur.code,
        code        = x.ml.value('@code', 'varchar(255)'),
        children    = x.ml.query('./value')
    from recur
    cross apply recur.children.nodes('value') x(ml)
)
select *
from recur
where parent_code = '121'
order by ordr

顺便说一句,你可以这样做,它会做你所期望的:

select x.ml.query('.')
from @Xml.nodes('value/value')x(ml)
order by row_number() over (order by x.ml)

为什么,如果这行得通,你不能order by x.ml直接没有row_number() over是超出我的。

于 2015-05-12T08:05:03.140 回答
3

erikkallen 的答案是绝对正确的。

但是,如果可以修改原始文档/模式,则另一种方法是将位置/索引存储在属性中。我混合使用这两种方法,具体取决于 XML 的“发起者”是谁以及需要对其执行的查询类型。归根结底,我对大多数 XML 的使用感到遗憾,除了 SQL Server 中可能的“哑存储”之外,当我可以将它 (XML) 转储为规范化表时,我通常会很高兴。

乐于应对“企业级”产品未提及的局限性——奇迹永无止境。

于 2011-01-12T06:12:33.450 回答
2

根据文档和此连接条目,这是不可能的,但连接条目包含两种解决方法。

我这样做:

WITH n(i) AS (SELECT 0 UNION SELECT 1 UNION SELECT 2 UNION SELECT 3 UNION SELECT 4 UNION SELECT 5 UNION SELECT 6 UNION SELECT 7 UNION SELECT 8 UNION SELECT 9),
     o(i) AS (SELECT n3.i * 100 + n2.i * 10 + n1.i FROM n n1, n n2, n n3)
SELECT v.value('@code', 'varchar(20)') AS code,
       v.value('../@code', 'varchar(20)') AS parent,
       o.i AS ord
  FROM o
 CROSS APPLY @xml.nodes('/root//value[sql:column("o.i")]') x(v)
 ORDER BY o.i
于 2009-07-15T22:04:45.993 回答
0

我看到了@Ben 的回答,然后......得到了新的解决方案

 row_number() over (order by (select null))

作为

  SELECT value.value('@code', 'varchar(20)') code, 
  value.value('../@code', 'varchar(20)') parent, 
  row_number() over (order by (select null))
  FROM @xml.nodes('/root//value') n(value)
于 2016-06-07T14:39:57.790 回答