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、現在のノード(.)を含むという名前の変数を定義します。current()これは基本的に、XQueryにXSLTのような関数がないことを回避するためのハックです。
  • この../*式は、現在のノードのすべての兄弟(親の子)を選択します。
  • [. << $i]述語は、兄弟のリストを現在のノード()の前()にあるものにフィルターし<<ます$i
  • count()先行する兄弟の数を計算し、1を加算して位置を取得します。このようにして、最初のノード(先行する兄弟がない)に1の位置が割り当てられます。
于 2012-03-25T19:12:25.383 に答える
6

x.nodes()次のような関数によって返されるxmlの位置を取得できます。

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の使用を拒否し、正規化されたテーブルにXMLをダンプできると通常は満足しています。

「エンタープライズグレード」の製品の言及されていない制限に喜んで対処します-不思議は決して終わりません。

于 2011-01-12T06:12:33.450 に答える
2

このドキュメントとこの接続エントリによると、それは不可能ですが、接続エントリには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 に答える