nodes() メソッド (xml データ型)
nodes() メソッドは、xml データ型のインスタンスをリレーショナル データに細分化するときに役立ちます。また、このメソッドにより、新しい行にマップされるノードを特定できます。
すべての xml データ型のインスタンスには、コンテキスト ノードが暗黙に用意されています。列や変数に格納された XML インスタンスの場合は、ドキュメント ノードがコンテキスト ノードになります。このドキュメント ノードは、すべての xml データ型のインスタンスの最上位に位置する暗黙のノードです。
nodes() メソッドの結果は、元の XML インスタンスの論理コピーを含む行セットです。このような論理コピーでは、クエリ式で識別されるいずれかのノードが、すべての行インスタンスのコンテキスト ノードに設定されます。その結果、その後に実行されるクエリはこのようなコンテキスト ノードへ相対移動できます。
行セットからは、複数の値を取得できます。たとえば、value() メソッドを nodes() で返される行セットに適用し、元の XML インスタンスから複数の値を取得できます。value() メソッドを XML インスタンスに適用すると、値は 1 つしか返されないことに注意してください。
次に一般的な構文を示します。
nodes (XQuery) as Table(Column)
- XQuery
XQuery 式の文字列リテラルです。このクエリ式でノードが構築されると、構築されるノードが結果の行セットで公開されます。クエリ式の結果が空のシーケンスの場合、結果の行セットは空になります。クエリ式で、ノードではなくアトミック値が含まれたシーケンスが静的に返される場合は、静的エラーが発生します。
- Table(Column)
結果の行セットのテーブル名と列名です。
たとえば、次のテーブルがあるとします。
T (ProductModelID int, Instructions xml)
次の製造手順ドキュメントがこのテーブルに格納されます。記載しているのはドキュメントの一部のみです。このドキュメントには 3 つの製造場所が含まれていることに注意してください。
<root>
<Location LocationID="10"...>
<step>...</step>
<step>...</step>
...
</Location>
<Location LocationID="20" ...>
...
</Location>
<Location LocationID="30" ...>
...
</Location>
</root>
クエリ式 /root/Location
を指定して nodes()
メソッドを呼び出すと、次のように 3 つの行を持つ行セットが返されます。各行には元の XML ドキュメントの論理コピーが格納されており、いずれかの <Location>
ノードがコンテキスト アイテムに設定されます。
Product
ModelID Instructions
----------------------------------
1 <root>
<Location LocationID="20" ... />
<Location LocationID="30" .../></root>
1 <root><Location LocationID="10" ... />
<Location LocationID="30" .../></root>
1 <root><Location LocationID="10" ... />
<Location LocationID="20" ... />
</root>
この行セットには、xml データ型のメソッドを使用してクエリを実行できます。次のクエリを実行すると、生成された行ごとにコンテキスト アイテムのサブツリーが抽出されます。
SELECT T2.Loc.query('.')
FROM T
CROSS APPLY Instructions.nodes('/root/Location') as T2(Loc)
次に結果を示します。
ProductModelID Instructions
----------------------------------
1 <Location LocationID="10" ... />
1 <Location LocationID="20" ... />
1 <Location LocationID="30" .../>
解説
返された行セットでは型情報が保持されることに注意してください。nodes() メソッドの結果には、query()、value()、exist()、nodes() など、xml データ型のメソッドを適用できます。ただし、modify() メソッドを適用して XML インスタンスを変更することはできません。
また、行セットのコンテキスト ノードは具体化できません。つまり、このコンテキスト ノードは SELECT ステートメントでは使用できません。ただし、IS NULL と COUNT(*) では使用できます。
nodes() メソッドを使用するシナリオは、OPENXML を使用する場合と同じです。したがって、XML の行セット ビューが提供されます。ただし、XML ドキュメントの複数の行が含まれるテーブルに nodes() メソッドを使用するときは、カーソルを使用する必要はありません。
nodes() メソッドで返される行セットには名前が付いていないことに注意してください。したがって、別名を使用してこれらの行セットに明示的に名前を付ける必要があります。
nodes() 関数をユーザー定義関数の結果に直接適用することはできません。ユーザー定義のスカラ関数の結果に対して nodes() を使用するには、ユーザー定義のスカラ関数の結果を変数に代入するか、派生テーブルを使用して列の別名をユーザー定義のスカラ関数の戻り値に代入した後、CROSS APPLY を使用し、この別名から選択します。
次の例では、CROSS APPLY
を使用し、ユーザー定義関数の結果から選択する方法を示します。
USE AdventureWorks;
GO
CREATE FUNCTION XTest()
RETURNS xml
AS
BEGIN
RETURN '<document/>';
END;
GO
SELECT A2.B.query('.')
FROM
(SELECT dbo.XTest()) AS A1(X)
CROSS APPLY X.nodes('.') A2(B);
GO
DROP FUNCTION XTest;
GO
例
A. xml 型の変数に対する nodes() メソッドの使用
次の例では、<Root
> 最上位要素と 3 つの <row
> 子要素が含まれる XML ドキュメントを使用します。このクエリでは、nodes()
メソッドを使用して、各 <row
> 要素に 1 つずつ別のコンテキスト ノードを設定します。nodes()
メソッドにより、3 つの行を持つ行セットが返されます。各行には元の XML の論理コピーが含まれ、それぞれのコンテキスト ノードで元のドキュメントの異なる <row
> 要素が識別されます。
その後、次のクエリを実行すると、各行のコンテキスト ノードが返されます。
DECLARE @x xml
SET @x='<Root>
<row id="1"><name>Larry</name><oflw>some text</oflw></row>
<row id="2"><name>moe</name></row>
<row id="3" />
</Root>'
SELECT T.c.query('.') AS result
FROM @x.nodes('/Root/row') T(c)
GO
次に結果を示します。この例では、クエリのメソッドでコンテキスト アイテムとそのコンテンツが返されます。
<row id="1"><name>Larry</name><oflw>some text</oflw></row>
<row id="2"><name>moe</name></row>
<row id="3"/>
次のように、親アクセサをコンテキスト ノードに適用すると、3 つのノードすべての <Root
> 要素が返されます。
SELECT T.c.query('..') AS result
FROM @x.nodes('/Root/row') T(c)
go
次に結果を示します。
<Root>
<row id="1"><name>Larry</name><oflw>some text</oflw></row>
<row id="2"><name>moe</name></row>
<row id="3" />
</Root>
<Root>
<row id="1"><name>Larry</name><oflw>some text</oflw></row>
<row id="2"><name>moe</name></row>
<row id="3" />
</Root>
<Root>
<row id="1"><name>Larry</name><oflw>some text</oflw></row>
<row id="2"><name>moe</name></row>
<row id="3" />
</Root>
次のクエリでは、絶対パスを指定します。絶対パス式を使用するコンテキスト ノードにクエリを実行すると、そのコンテキスト ノードのルート ノードから照合が開始されます。したがって、nodes()
から返されるコンテキスト ノードごとに、3 つの行すべてが返されることになります。
SELECT T.c.query('/Root/row') AS result
FROM @x.nodes('/Root/row') T(c)
GO
<row id="1"><name>Larry</name><oflw>some text</oflw></row>
<row id="2"><name>moe</name></row>
<row id="3" />
<row id="1"><name>Larry</name><oflw>some text</oflw></row>
<row id="2"><name>moe</name></row>
<row id="3" />
<row id="1"><name>Larry</name><oflw>some text</oflw></row>
<row id="2"><name>moe</name></row>
<row id="3" />
xml データ型の nodes()
メソッドから返される列を直接使用できないことに注意してください。たとえば、次のクエリではエラーが返されます。
...
SELECT T.c
FROM @x.nodes('/Root/row') T(c)
次のクエリでは、xml データ型の value()
メソッドと query()
メソッドを、nodes()
メソッドから返される行セットに適用します。value()
メソッドによりコンテキスト アイテム (<row
>) の id
属性が返され、query()
メソッドによりコンテキスト アイテムの <name
> 要素サブツリーが返されます。
DECLARE @x xml
SET @x='
<Root>
<row id="1"><name>Larry</name><oflw>some text</oflw></row>
<row id="2"><name>Joe</name></row>
<row id="3" />
</Root>
'
SELECT T.c.value('@id','int') as id,
T.c.query('name') as NAME
FROM @x.nodes('/Root/row') T(c)
GO
次に結果を示します。
id NAME
-----------------------
1 <name>Larry</name>
2 <name>Joe</name>
3
結果に行 ID 3
が含まれていて、その <row
> 要素に <name
> 子要素が含まれていないことに注意してください。この結果をフィルタ選択して、<name
> 子要素を持たない行を返したり、返さないようにする場合は、次のいずれかの方法でフィルタ選択を実行できます。
nodes()
パス式で、/Root/row[name]
などの述語を使用します。- 行セットの exist() メソッドを使用します。
- CROSS APPLY を使用します。
- OUTER APPLY を使用します。
次のクエリでは、nodes()
で返される行セットに対して、exist()
メソッドを指定しています。コンテキスト ノード (<row
>) に <name
> 子要素が含まれている場合は、exist()
メソッドにより True が返されます。
DECLARE @x xml
SET @x='<Root>
<row id="1"><name>Larry</name><oflw>some text</oflw></row>
<row id="2"><name>Joe</name></row>
<row id="3" />
</Root>'
SELECT T1.rows.value('@id','int') as id
FROM @x.nodes('/Root/row') T1(rows)
WHERE T1.rows.exist('name') = 1;
GO
この場合、行 ID 1 と 2 の 2 行が返されます。
次のクエリでは OUTER APPLY
を使用しています。OUTER APPLY
は T1(rows)
の各行に nodes()
を割り当て、結果セットと NULL を生成する行を返します。したがって、WHERE 句を使用して行をフィルタ選択し、T2.names
列が NULL 以外の行のみを取得します。
DECLARE @x xml
SET @x='
<Root>
<row id="1"><name>Larry</name><oflw>some text</oflw></row>
<row id="2"><name>Joe</name></row>
<row id="3" />
</Root>'
SELECT T1.rows.value('@id','int') as id
FROM @x.nodes('/Root/row') T1(rows)
OUTER APPLY T1.rows.nodes('./name') as T2(names)
WHERE T2.names IS NOT NULL
GO
次のクエリでは CROSS APPLY
を使用しています。CROSS APPLY
では、外部テーブル T1(rows)
の各行に nodes()
が適用され、nodes()
が T1.rows
に適用されたときに、結果セットを生成する行のみが返されます。この場合、IS NOT NULL をテストするために、WHERE 句を使用する必要はありません。
DECLARE @x xml
SET @x='<Root>
<row id="1"><name>Larry</name><oflw>some text</oflw></row>
<row id="2"><name>Joe</name></row>
<row id="3" />
</Root>'
SELECT T1.rows.value('@id','int') as id
FROM @x.nodes('/Root/row') T1(rows)
CROSS APPLY T1.rows.nodes('./name') as T2(names)
GO
CROSS APPLY と OUTER APPLY の詳細については、「APPLY の使用」を参照してください。
B. xml 型の列に対する nodes() メソッドの指定
この例では、ProductModel テーブルの xml 型の Instructions 列に格納されている、自転車の製造手順を使用します。詳細については、「AdventureWorks データベースの xml データ型表現」を参照してください。
次の例では、ProductModel
テーブルの xml 型の Instructions
列に対して nodes()
メソッドを指定します。
/MI:root/MI:Location
パスを指定することで、nodes()
メソッドでは <Location
> 要素がコンテキスト ノードに設定されます。結果の行セットには、ドキュメントの各 <Location
> ノードに 1 つずつ元のドキュメントの論理コピーが含まれます。また、その <Location
> 要素がコンテキスト ノードに設定されます。したがって、nodes()
関数から返されるのは、<Location
> コンテキスト ノードのセットです。
この行セットに対する query()
メソッドでは self::node
が要求されるため、各行の <Location>
要素が返されます。
この例のクエリでは、各 <Location
> 要素が、特定の製品モデルの製造手順ドキュメントのコンテキスト ノードとして設定されます。これらのコンテキスト ノードを使用して、次のような値を取得できます。
- 各 <
Location
> の場所 ID - 各 <
Location
> の製造手順 (<step
> 子要素)
このクエリでは、query()
メソッドで self::node()
に対する省略構文 '.'
を指定して、コンテキスト アイテムを返しています。
次の点に注意してください。
nodes()
メソッドは Instructions 列に適用され、行セットT (C)
を返します。この行セットには、/root/Location
をコンテキスト アイテムとして、元の製造手順ドキュメントの論理コピーが格納されています。CROSS APPLY により、
Instructions
テーブルの各行にnodes()
が適用され、結果セットを生成する行のみが返されます。SELECT C.query('.') as result FROM Production.ProductModel CROSS APPLY Instructions.nodes(' declare namespace MI="https://schemas.microsoft.com/sqlserver/2004/07/adventure-works/ProductModelManuInstructions"; /MI:root/MI:Location') as T(C) WHERE ProductModelID=7
結果の一部を次に示します。
<MI:Location LocationID="10" ...> <MI:step ... /> ... </MI:Location> <MI:Location LocationID="20" ... > <MI:step ... /> ... </MI:Location> ...
次のクエリは前のクエリに似ていますが、行セットのコンテキスト ノードを使用して値のセットを取得するために、value()
と query()
を使用するという点が異なります。SELECT
句により、各場所の場所 ID とその場所で使用されるツールが取得されます。
SELECT C.value('@LocationID','int') as LId,
C.query('declare namespace MI="https://schemas.microsoft.com/sqlserver/2004/07/adventure-works/ProductModelManuInstructions";
MI:step/MI:tool') as result
FROM Production.ProductModel
CROSS APPLY Instructions.nodes('
declare namespace MI="https://schemas.microsoft.com/sqlserver/2004/07/adventure-works/ProductModelManuInstructions";
/MI:root/MI:Location') as T(C)
WHERE ProductModelID=7
次に結果を示します。読みやすくするために名前空間は省略しています。
LId result
10 <MI:tool xmlns:MI="...">T-85A framing tool</MI:tool>
<MI:tool xmlns:MI="...">Trim Jig TJ-26</MI:tool>
<MI:tool xmlns:MI="...">router with a carbide tip 15</MI:tool>
<MI:tool xmlns:MI="...">Forming Tool FT-15</MI:tool>
20
30 <MI:tool xmlns:MI="...">standard debur tool</MI:tool>
45 <MI:tool xmlns:MI="...">paint harness</MI:tool>
50
60
C. 別の nodes() メソッドにより返された行セットに対する nodes() の適用
次のコードでは、XML ドキュメントに対し、ProductModel
テーブルの Instructions
列に格納されている製造手順を照会しています。このクエリでは、製品モデル ID、製造場所、および製造手順が含まれた行セットが返されます。
次の点に注意してください。
nodes()
メソッドはInstructions
列に適用され、T1 (Locations)
行セットを返します。この行セットには、/root/Location
をコンテキスト アイテムとして、元の製造手順ドキュメントの論理コピーが格納されています。nodes()
はT1 (Locations)
行セットに適用され、T2 (steps)
行セットを返します。この行セットには、/root/Location/step
をコンテキスト アイテムとして、元の製造手順ドキュメントの論理コピーが格納されています。
SELECT ProductModelID, Locations.value('./@LocationID','int') as LocID,
steps.query('.') as Step
FROM Production.ProductModel
CROSS APPLY Instructions.nodes('
declare namespace MI="https://schemas.microsoft.com/sqlserver/2004/07/adventure-works/ProductModelManuInstructions";
/MI:root/MI:Location') as T1(Locations)
CROSS APPLY T1.Locations.nodes('
declare namespace MI="https://schemas.microsoft.com/sqlserver/2004/07/adventure-works/ProductModelManuInstructions";
./MI:step ') as T2(steps)
WHERE ProductModelID=7
GO
次に結果を示します。
ProductModelID LocID Step
----------------------------
7 10 <step ... />
7 10 <step ... />
...
7 20 <step ... />
7 20 <step ... />
7 20 <step ... />
...
このクエリでは、MI
プレフィックスを 2 回宣言しています。代わりに WITH XMLNAMESPACES
を使用し、このプレフィックスを 1 回宣言してクエリで使用することもできます。
WITH XMLNAMESPACES (
'https://schemas.microsoft.com/sqlserver/2004/07/adventure-works/ProductModelManuInstructions' AS MI)
SELECT ProductModelID, Locations.value('./@LocationID','int') as LocID,
steps.query('.') as Step
FROM Production.ProductModel
CROSS APPLY Instructions.nodes('
/MI:root/MI:Location') as T1(Locations)
CROSS APPLY T1.Locations.nodes('
./MI:step ') as T2(steps)
WHERE ProductModelID=7
GO
次のクエリは前のクエリに似ていますが、exist()
メソッドを T2(steps)
行セットの XML に適用して、1 つ以上の製造ツールを使用する製造手順のみを取得するという点が異なります。つまり、<step
> 要素には少なくとも 1 つの <tool
> 子要素が含まれます。
WITH XMLNAMESPACES (
'https://schemas.microsoft.com/sqlserver/2004/07/adventure-works/ProductModelManuInstructions' AS MI)
SELECT ProductModelID,
Locations.value('./@LocationID','int') as LocID,
steps.query('.') as Steps
FROM Production.ProductModel
CROSS APPLY Instructions.nodes('/MI:root/MI:Location') as T1(Locations)
CROSS APPLY T1.Locations.nodes('./MI:step') as T2(steps)
WHERE ProductModelID=7
AND steps.exist('./MI:tool') = 1
GO
参照
概念
WITH XMLNAMESPACES を使用した名前空間の追加
xml データ型
XML インスタンスの生成
サンプル XML アプリケーション