web-dev-qa-db-ja.com

XMLフラグメントからデータを抽出する

SQL Server 2014では、次のxmlフラグメントがあります。

<salesorder>
   <ordnum>123</ordnum>
   <orddate>2017-04-17</orddate>
   <details>
      <detail>
         <itemtype>Stationery</itemtype>
         <itemdesc>Pencil</itemdesc>
         <specifications>
            <specification>
               <name>Qty</name>
               <value>5</value>
            </specification>
            <specification>
               <name>Description</name>
               <value>Pencil H1</value>
            </specification>
            <specification>
               <name>Price</name>
               <value>1.50</value>
            </specification>
         </specifications>
      </detail>
      <detail>
         <itemtype>Stationery</itemtype>
         <itemdesc>Pencil</itemdesc>
         <specifications>
            <specification>
               <name>Qty</name>
               <value>10</value>
            </specification>
            <specification>
               <name>Description</name>
               <value>Pencil H2</value>
            </specification>
            <specification>
               <name>Colour</name>
               <value>Red</value>
            </specification>
            <specification>
               <name>Price</name>
               <value>2.75</value>
            </specification>
         </specifications>
      </detail>
   </details>
</salesorder>

次のデータを抽出したいと思います。

ordnum   itemtype     qty   price
123      Stationery   5     1.50
123      Stationery   10    2.75

これはT-SQLで実現できますか?

事前にありがとう、MP

1
Mel

変数_@X_にXMLを入れます。

_select @X.value('(salesorder/ordnum/text())[1]', 'int') as ordnum,
       T.X.value('(itemtype/text())[1]', 'varchar(15)') as itemtype,
       T.X.value('(specifications/specification[name/text() = "Qty"]/value/text())[1]', 'int') as qty,
       T.X.value('(specifications/specification[name/text() = "Price"]/value/text())[1]', 'money') as price
from @X.nodes('/salesorder/details/detail') as T(X);
_

このクエリの最も複雑な部分は、おそらくQtyPriceの値を取得するために使用されるXQuery式です。

[name/text() = "Qty"]は、ノードnameの値を調べ、Qtyの場合はtrueを返す述語です。

残りは nodes() を使用してXMLを_/salesorder/details/detail_で細断して詳細要素ごとに1行を取得し、 value() を使用して値を抽出する基本的なXMLクエリです。あなたはあなたのXMLから欲しいです。

1
Mikael Eriksson

以下のt-sqlコードは確かにあなたを助けます。

DECLARE @vXML XML;
SET @vXML = '<salesorder>
   <ordnum>123</ordnum>
   <orddate>2017-04-17</orddate>
   <details>
      <detail>
         <itemtype>Stationery</itemtype>
         <itemdesc>Pencil</itemdesc>
         <specifications>
            <specification>
               <name>Qty</name>
               <value>5</value>
            </specification>
            <specification>
               <name>Description</name>
               <value>Pencil H1</value>
            </specification>
            <specification>
               <name>Price</name>
               <value>1.50</value>
            </specification>
         </specifications>
      </detail>
      <detail>
         <itemtype>Stationery</itemtype>
         <itemdesc>Pencil</itemdesc>
         <specifications>
            <specification>
               <name>Qty</name>
               <value>10</value>
            </specification>
            <specification>
               <name>Description</name>
               <value>Pencil H2</value>
            </specification>
            <specification>
               <name>Colour</name>
               <value>Red</value>
            </specification>
            <specification>
               <name>Price</name>
               <value>2.75</value>
            </specification>
         </specifications>
      </detail>
   </details>
</salesorder>';
SELECT  
       salesorder.Col.value('ordnum[1]', 'int') 
       , detail.Col.value('itemtype[1]', 'varchar(255)')
       , specification_qty.Qty AS Qty
       , specification_price.Price AS Price
FROM @vXML.nodes('//salesorder') AS salesorder(Col)
    CROSS APPLY salesorder.Col.nodes('details') AS details(Col)
    CROSS APPLY details.Col.nodes('detail') AS detail(Col)
    CROSS APPLY detail.Col.nodes('specifications') AS specifications(Col)
    CROSS APPLY (SELECT specification.Col.value('value[1]', 'int') AS Qty FROM specifications.Col.nodes('specification') AS specification(Col) WHERE specification.Col.value('name[1]','VARCHAR(255)') = 'Qty') AS specification_qty
    CROSS APPLY (SELECT specification.Col.value('value[1]', 'decimal(3,2)') AS Price FROM specifications.Col.nodes('specification') AS specification(Col) WHERE specification.Col.value('name[1]','VARCHAR(255)') = 'Price') AS specification_price

ソリューションに関する簡単な説明:

提供されるXMLはマルチレベルXMLであるため、データをチャンクで処理する必要があります。したがって、必要な結果を得るために、複数のレベルのxmlを複数のテーブルに分離し、それらをクロス適用する必要があります。単一レベルのXMLデータの場合、単一のSELECT * FROM Tblメソッドが機能していた可能性があります。

参照リンク:

Sanchitosがスタックオーバーフローで提供する単一レベルのXMLソリューション

スタックオーバーフローでGeorge Tが提供するマルチレベルXMLソリューション

クロスアプライを使用したSQL Serverのさまざまな階層レベルのXMLのクエリ

これがお役に立てば幸いです。

1
SwapnilBhate