SQL Server 2008のXMLフィールドから値を選択する


112

XMLフィールドを見るだけで、行は次のようになります。

<person><firstName>Jon</firstName><lastName>Johnson</lastName></person>
<person><firstName>Kathy</firstName><lastName>Carter</lastName></person>
<person><firstName>Bob</firstName><lastName>Burns</lastName></person>

これらは私のテーブルの3つの行であることに注意してください。

次のようにSQL結果をテーブルとして返したい

Jon  | Johnson
Kathy| Carter
Bob  | Burns

これを実行するクエリは何ですか?


xmlのすべての要素を取得する方法はありませんか?1つずつ指定する必要がありますか?これは本当に退屈な作業です。「テーブルから選択」を実行できます。必要なすべての要素を指定する必要なく、「XMLから選択」を実行できるはずです。
キースタイラー

回答:


157

XMLフィールドの名前が 'xmlField'だとすると...

SELECT 
[xmlField].value('(/person//firstName/node())[1]', 'nvarchar(max)') as FirstName,
[xmlField].value('(/person//lastName/node())[1]', 'nvarchar(max)') as LastName
FROM [myTable]

16
xmlFieldに複数の<person>要素が含まれている場合は、.nodes()とクロス適用を使用する必要があります。
Remus Rusanu 2009年

SQL Server 2008 R2 Expressは、ソリューションで次のエラーを返しましたThe XQuery syntax '/function()' is not supported.。一方、@ Remus Rusanuはそれを行うようです:)
RMiranda 2013

2
奇妙な。これは102回投票されましたが、この回答は最初の XMLレコードからのデータのみを返します。そして、それはいくつかの[myTable]テーブルを参照しています...それはどこから来たのですか?
Mike Gledhill 2016

私はこれを何度も試しましたが、うまくいきませんでした。私のXMLは<BAM><Type>Electrical</Type><BaIds><a:int>7330</a:int></BaIds></BAM>、私の選択はselect e.MessageData.value('(/BAM/Type)[1]', 'varchar(100)')です。私はまた、選択しようとしているe.MessageData.value('(/BAM/Type/node())[1]', 'varchar(100)')、と'(//Type/node())[1]''(./Type)[1]'と、私は考えることができる他のすべての組み合わせ。私が今までに得ることはすべてですNULL
JonathanPeel

1
@MikeGledhillでは、複数のXMLレコードから値が返されます。また、OPが指定するテーブルの唯一の名前は「my table」です:)
Paul

123

XMLデータがテーブル「テーブル」から取得され、列「フィールド」に格納されていることを考慮してください。XMLメソッドを使用し、で値を抽出しxml.value()、でプロジェクトノードをxml.nodes()使用CROSS APPLYし、結合に使用します。

SELECT 
    p.value('(./firstName)[1]', 'VARCHAR(8000)') AS firstName,
    p.value('(./lastName)[1]', 'VARCHAR(8000)') AS lastName
FROM table 
    CROSS APPLY field.nodes('/person') t(p)

あなたは捨てることができますnodes()し、cross apply各フィールドは、1つの要素「人」が含まれている場合。XMLが変数であり、選択FROM @variable.nodes(...)する必要がない場合、は必要ありませんcross apply


1
この方法がいかに効率的か、もっと良い方法があるかどうか疑問に思います。XPath結果と組み合わせたCROSS APPLYは、リソースを大量に消費するクエリになる可能性があるようです。
redcalx 2010年

1
@thelocster:これは通常のデータアクセスと同じです。XMLのパフォーマンスを向上させる手法は、十分に文書化されています。msdn.microsoft.com/en-us/library/ms345118%28SQL.90%29.aspx
Remus Rusanu

2
XMLにxmlns名前空間が定義されている場合は、上記のXQuery(XPath)式でそれらを定義する必要があることに注意してください。例については、stackoverflow.com / a / 1302150/656010を参照してください。
トムウェイソン、2012年

私が必要としていたものとは少し異なりますが、これは私が抱えていた問題であるXML列のある複数の行に対する完璧な解決策でした-行をループしてXML列内からデータフィールドを引き出し、それらを配置したかったのです。挿入ステートメント。したがって、XMLフィールドの3列のデータごとに5行= 15挿入、完全です。
dan richardson、2012

17

この投稿は、XML形式が少し異なる問題を解決するのに役立ちました... XMLには次の例のようなキーのリストが含まれており、DeleteBatchという名前のテーブルのSourceKeys列にXMLを格納します。

<k>1</k>
<k>2</k>
<k>3</k>

テーブルを作成し、いくつかのデータを入力します。

CREATE TABLE dbo.DeleteBatch (
    ExecutionKey INT PRIMARY KEY,
    SourceKeys XML)

INSERT INTO dbo.DeleteBatch ( ExecutionKey, SourceKeys )
SELECT 1, 
    (CAST('<k>1</k><k>2</k><k>3</k>' AS XML))

INSERT INTO dbo.DeleteBatch ( ExecutionKey, SourceKeys )
SELECT 2, 
    (CAST('<k>100</k><k>101</k>' AS XML))

XMLからキーを選択するためのSQLは次のとおりです。

SELECT ExecutionKey, p.value('.', 'int') AS [Key]
FROM dbo.DeleteBatch
    CROSS APPLY SourceKeys.nodes('/k') t(p)

これがクエリ結果です...

ExecutionKeyキー
1 1
1 2
1 3
2 100
2 101

9

これはあなたの質問に答えるかもしれません:

select cast(xmlField as xml) xmlField into tmp from (
select '<person><firstName>Jon</firstName><lastName>Johnson</lastName></person>' xmlField
union select '<person><firstName>Kathy</firstName><lastName>Carter</lastName></person>'
union select '<person><firstName>Bob</firstName><lastName>Burns</lastName></person>'
) tb

SELECT
    xmlField.value('(person/firstName)[1]', 'nvarchar(max)') as FirstName
    ,xmlField.value('(person/lastName)[1]', 'nvarchar(max)') as LastName
FROM tmp

drop table tmp

6

臭い。これは発見するのに本当に便利なスレッドでした。

私はまだこれらの提案のいくつかを混乱させていました。文字列でvaluewith を使用すると[1]、最初の値のみが取得されます。そして、いくつかの提案を使用することをお勧めしますcross applyは、(私のテストでは)whichして、あまりにも多くのデータを返しただけであるとた。

そこで、xmlオブジェクトを作成し、その値をテーブルに読み取る方法の簡単な例を次に示します。

DECLARE @str nvarchar(2000)

SET @str = ''
SET @str = @str + '<users>'
SET @str = @str + '  <user>'
SET @str = @str + '     <firstName>Mike</firstName>'
SET @str = @str + '     <lastName>Gledhill</lastName>'
SET @str = @str + '     <age>31</age>'
SET @str = @str + '  </user>'
SET @str = @str + '  <user>'
SET @str = @str + '     <firstName>Mark</firstName>'
SET @str = @str + '     <lastName>Stevens</lastName>'
SET @str = @str + '     <age>42</age>'
SET @str = @str + '  </user>'
SET @str = @str + '  <user>'
SET @str = @str + '     <firstName>Sarah</firstName>'
SET @str = @str + '     <lastName>Brown</lastName>'
SET @str = @str + '     <age>23</age>'
SET @str = @str + '  </user>'
SET @str = @str + '</users>'

DECLARE @xml xml
SELECT @xml = CAST(CAST(@str AS VARBINARY(MAX)) AS XML) 

--  Iterate through each of the "users\user" records in our XML
SELECT 
    x.Rec.query('./firstName').value('.', 'nvarchar(2000)') AS 'FirstName',
    x.Rec.query('./lastName').value('.', 'nvarchar(2000)') AS 'LastName',
    x.Rec.query('./age').value('.', 'int') AS 'Age'
FROM @xml.nodes('/users/user') as x(Rec)

そしてここに出力があります:

ここに画像の説明を入力してください

奇妙な構文ですが、適切な例を使用すれば、独自のSQL Server関数に追加するのは簡単です。

そういえば、これがこの質問に対する正しい答えです。

@xmlタイプの変数にxmlデータがあると仮定するとxml(上記の例で示したように)、質問で引用されたxmlから3行のデータを返す方法は次のとおりです。

SELECT 
    x.Rec.query('./firstName').value('.', 'nvarchar(2000)') AS 'FirstName',
    x.Rec.query('./lastName').value('.', 'nvarchar(2000)') AS 'LastName'
FROM @xml.nodes('/person') as x(Rec)

ここに画像の説明を入力してください


これが正解かわかりません。OPは、XML型のテーブルから列をクエリすることを求めています。その場合[1]、インデックス序数を使用して強制的に1行を返すか、列をクロス適用してnodes()を取得する必要があります。 xpathを実行できる構造。あなたのコードは多くの修正なしではそのシナリオに変換されません。テーブル列ではなく変数を使用しています。また、query()xmlを返す関数を使いすぎています。例:x.Rec.value('(./firstName)[1]', 'nvarchar(2000)') AS FirstName
ダボス

3

XMLをルート要素でラップできる場合-たとえば、次の解決策があります。

DECLARE @PersonsXml XML = '<persons><person><firstName>Jon</firstName><lastName>Johnson</lastName></person>
<person><firstName>Kathy</firstName><lastName>Carter</lastName></person>
<person><firstName>Bob</firstName><lastName>Burns</lastName></person></persons>'

SELECT  b.value('(./firstName/text())[1]','nvarchar(max)') as FirstName, b.value('(./lastName/text())[1]','nvarchar(max)') as LastName
FROM @PersonsXml.nodes('/persons/person') AS a(b)

ここに画像の説明を入力してください


3

MSSQLは、次のように通常のXPathルールを使用します。

  • nodename「nodename」という名前のすべてのノードを選択します
  • /ルートノードから選択します
  • //現在のノードからドキュメント内のノードを選択します。ノードはどこにあっても選択に一致します
  • 。現在のノードを選択します
  • ..現在のノードの親を選択
  • @属性を選択します

W3Schools


2
SELECT 
cast(xmlField as xml).value('(/person//firstName/node())[1]', 'nvarchar(max)') as FirstName,
cast(xmlField as xml).value('(/person//lastName/node())[1]', 'nvarchar(max)') as LastName
FROM [myTable]

0

/ *この例では、スキーマを持つXML変数を使用します* /

IF EXISTS (SELECT * FROM sys.xml_schema_collections 
           WHERE name = 'OrderingAfternoonTea')
BEGIN
    DROP XML SCHEMA COLLECTION dbo.OrderingAfternoonTea 
END
GO

CREATE XML SCHEMA COLLECTION dbo.OrderingAfternoonTea AS
N'<?xml version="1.0" encoding="UTF-16" ?>
  <xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema"
     targetNamespace="http://Tfor2.com/schemas/actions/orderAfternoonTea"
     xmlns="http://Tfor2.com/schemas/actions/orderAfternoonTea"
     xmlns:TFor2="http://Tfor2.com/schemas/actions/orderAfternoonTea"
     elementFormDefault="qualified"
     version="0.10"
   > 
    <xsd:complexType name="AfternoonTeaOrderType">
       <xsd:sequence>
         <xsd:element name="potsOfTea" type="xsd:int"/>
         <xsd:element name="cakes" type="xsd:int"/>
         <xsd:element name="fruitedSconesWithCream" type="xsd:int"/>
         <xsd:element name="jams" type="xsd:string"/>
      </xsd:sequence>
      <xsd:attribute name="schemaVersion" type="xsd:long" use="required"/>
    </xsd:complexType>

    <xsd:element name="afternoonTeaOrder"
                 type="TFor2:AfternoonTeaOrderType"/>

  </xsd:schema>' ;
GO

DECLARE @potsOfTea int;
DECLARE @cakes int;
DECLARE @fruitedSconesWithCream int;
DECLARE @jams nvarchar(128);

DECLARE @RequestMsg NVARCHAR(2048);
DECLARE @RequestXml XML(dbo.OrderingAfternoonTea);

set @potsOfTea = 5;
set @cakes = 7;
set @fruitedSconesWithCream = 25;
set @jams = N'medlar jelly, quince and mulberry';

SELECT @RequestMsg = N'<?xml version="1.0" encoding="utf-16" ?>
<TFor2:afternoonTeaOrder schemaVersion="10"
    xmlns:TFor2="http://Tfor2.com/schemas/actions/orderAfternoonTea">
    <TFor2:potsOfTea>' + CAST(@potsOfTea as NVARCHAR(20)) 
        + '</TFor2:potsOfTea>
    <TFor2:cakes>' + CAST(@cakes as NVARCHAR(20)) + '</TFor2:cakes>
    <TFor2:fruitedSconesWithCream>' 
        + CAST(@fruitedSconesWithCream as NVARCHAR(20))
        + '</TFor2:fruitedSconesWithCream>
    <TFor2:jams>' + @jams + '</TFor2:jams>
</TFor2:afternoonTeaOrder>';

SELECT @RequestXml  = CAST(CAST(@RequestMsg AS VARBINARY(MAX)) AS XML) ;

with xmlnamespaces('http://Tfor2.com/schemas/actions/orderAfternoonTea'
                    as tea)
select
    cast( x.Rec.value('.[1]/@schemaVersion','nvarchar(20)') as bigint )
        as schemaVersion,
    cast( x.Rec.query('./tea:potsOfTea')
               .value('.','nvarchar(20)') as bigint ) as potsOfTea,
    cast( x.Rec.query('./tea:cakes')
               .value('.','nvarchar(20)') as bigint )  as cakes,
    cast( x.Rec.query('./tea:fruitedSconesWithCream')
               .value('.','nvarchar(20)') as bigint ) 
      as fruitedSconesWithCream,
    x.Rec.query('./tea:jams').value('.','nvarchar(50)')  as jams
from @RequestXml.nodes('/tea:afternoonTeaOrder')  as x(Rec);

select @RequestXml.query('/*')
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.