2009-07-15 21 views
15

XML文書内のノードの順序はどのようにして見つけることができますか?SQL Serverでの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> 

と私はXMLからの値の順序を保存

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

のように定義されたテーブルにこの事を取得しようとしていますドキュメント(コードで注文することはできません)。私は

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セレクタ内でのみ使用できます)。

何とか可能ですが、どうですか?

+0

として新しいsollution入手?もしそうでなければ、それは痛みになるでしょう。あなたがコードに頼ることはできませんか? – gbn

+0

... xmlからどのような出力が必要ですか? – gbn

+0

質問を更新して詳細を提供しました。そして、いいえ、無限の深さがあります。 – erikkallen

答えて

31

あなたは、各ノードの前の兄弟ノード数カウントすることにより、position()機能をエミュレートすることができますどのように動作

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 

:ここ

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) 

は、結果セットです

  • for $i in .句は、現在のノード(.)を含む$iという名前の変数を定義します。これは基本的に、XSLTのようなXSLTのような機能がないXQueryを回避するためのハックです。current()
  • ../*式は、現在のノードのすべての兄弟(親の子)を選択します。
  • [. << $i]述語は、現在のノード($i)に先行するもの(<<)に兄弟のリストをフィルタリングします。
  • count()先行する兄弟の数。次に、1を加えて位置を取得します。例えば

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

    :そのように(何前の兄弟を有さない)最初のノードは、ユーザーがそうようx.nodes()関数によって返されたXMLの位置を得ることができる。1.

+2

かなり大きなXMLファイルでこのコードを使用しました。戻り値(../* [。<< $ i])+ 1'部分は、これが永遠に取ったすべてのノードに先行するすべての「兄弟」ノードを横断します(私たちは家に帰る間に仕事をします。したがって、このコードはO(n^2)の効率があることに注意してください。 – funkwurm

2

this文書とこのconnect entryによると、Connectエントリには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 
+0

毎回私はこれを実行する良い方法があるかどうかを試してみる私はいつも泣いて感じる。私が見つけた唯一の方法です(実際には数値表を使用しますが、同じ醜いハック)。これは、XMLをサポートし、簡単なシュレッダーとアクセスを必要以上に複雑にするサーバーにとって、絶対に哀れな言い訳です。 。 –

3

erikkallenによって答えが絶対的に正しいです:

私はこのようにそれを行います。

ただし、元の文書/スキーマを変更できる場合は、位置/インデックスを属性に格納することもできます。私はXMLの「創始者」が誰であるのか、そしてそれに実行される必要のあるクエリのタイプに応じて、両方のアプローチの組み合わせを使用します。私はSQL Serverでおそらく "ダムストレージ"を除いて、ほとんどのXMLの使用を控えています。正規化されたテーブルのためにXMLをダンプすることができます。

"エンタープライズグレード"の製品に言及していない制限に満足しています - 不思議は決して終わりません。

+0

あなたの「あまり使われていないXML」のために+1してください。ノードを選択しなくても、それらのノードを更新する必要がないときは、本当にうんざりです。 –

4

の位置が割り当てられています:

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) 
+1

おかげさまで@Ben、私は新しい解決法row_number()over(order by(select null)) –

+0

@nick_n_a、素敵です。 – Ben

+0

@nick_n_a、あなたの考えに基づいて更新されました。 – Ben

2

SQL Serverのrow_number()は、実際に注文するxml-nodes列を受け入れます。

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

なぜ、これが動作するかどうか、あなたは:さておき、あなたがこれを行うことができ、それはあなたが期待する何をすべきかやるよう

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 

:あなたがこれを行うことができますrecursive CTEと組み合わせますちょうどorder by x.mlrow_number() overなしで直接私を超えていることはできません。私は@Benとによって答えを見る

0

...あなたはノードの有限の深さを持っていますか

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)