Wählen Sie Werte aus dem XML-Feld in SQL Server 2008 aus


111

Wenn ich nur mein XML-Feld betrachte, sehen meine Zeilen folgendermaßen aus:

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

Beachten Sie, dass dies drei Zeilen in meiner Tabelle sind.

Ich möchte ein SQL-Ergebnis als Tabelle wie in zurückgeben

Jon  | Johnson
Kathy| Carter
Bob  | Burns

Welche Abfrage wird dies erreichen?


Gibt es keine Möglichkeit, einfach ALLE Elemente in der XML abzurufen? Sie müssen eins nach dem anderen angeben? Das wird sehr schnell langweilig. Sie können "select * from table" ausführen, anscheinend sollten Sie in der Lage sein, "select xml. * From xml" auszuführen, ohne jedes einzelne gewünschte Element angeben zu müssen.
Keith Tyler vor

Antworten:


156

Vorausgesetzt, das XML-Feld heißt '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
Sie müssen .nodes () und cross apply verwenden, wenn xmlField mehr als ein <person> -Element enthält.
Remus Rusanu

SQL Server 2008 R2 Express hat mir diesen Fehler mit Ihrer Lösung zurückgegeben : The XQuery syntax '/function()' is not supported.; Auf der anderen Seite scheint @Remus Rusanu es zu tun :)
RMiranda

2
Bizarr. Dies wurde 102 Mal abgestimmt, aber diese Antwort gibt nur Daten aus dem ersten XML-Datensatz zurück. Und es bezieht sich auf eine [myTable] -Tabelle ... woher kam das ?!
Mike Gledhill

Ich habe das so oft versucht und es nie funktionieren lassen. Mein XML ist <BAM><Type>Electrical</Type><BaIds><a:int>7330</a:int></BaIds></BAM>, meine Auswahl ist select e.MessageData.value('(/BAM/Type)[1]', 'varchar(100)'). Ich habe auch select e.MessageData.value('(/BAM/Type/node())[1]', 'varchar(100)'), and '(//Type/node())[1]', '(./Type)[1]'und jede andere Kombination ausprobiert , die mir einfällt. Alles was ich jemals bekomme ist NULL.
Jonathan

1
@MikeGledhill gibt Werte aus mehreren XML-Datensätzen zurück, die für mich in Ordnung sind. Auch der einzige Name für die Tabelle, die das OP gibt, ist "meine Tabelle" :)
Paul

122

Berücksichtigen Sie, dass XML-Daten aus einer Tabelle 'Tabelle' stammen und in einer Spalte 'Feld' gespeichert sind: Verwenden Sie die XML-Methoden , extrahieren Sie Werte mit xml.value(), Projektknoten mit xml.nodes(), verwenden Sie CROSS APPLYzum Verbinden:

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)

Sie können das nodes()und weglassen, cross applywenn jedes Feld genau ein Element 'Person' enthält. Wenn das XML eine Variable ist, die Sie auswählen FROM @variable.nodes(...)und die Sie nicht benötigen cross apply.


1
Ich frage mich, wie effizient diese Methode ist und ob es einen besseren Weg gibt. Die Kombination aus CROSS APPLY und XPath-Ergebnissen scheint zu einer ressourcenhungrigen Abfrage zu führen.
Redcalx

1
@thelocster: Dies unterscheidet sich nicht vom normalen Datenzugriff. Techniken zur Verbesserung der XML-Leistung sind gut dokumentiert. msdn.microsoft.com/en-us/library/ms345118%28SQL.90%29.aspx
Remus Rusanu

2
Beachten Sie, dass Sie in XML, wenn in XML XML-Namespaces definiert sind, diese im obigen XQuery-Ausdruck (XPath) definieren müssen. Ein Beispiel finden Sie unter stackoverflow.com/a/1302150/656010 .
Tom Wayson

Etwas anders als ich es brauchte, aber dies war eine perfekte Lösung für ein Problem, das ich hatte, nämlich mehrere Zeilen mit einer XML-Spalte - ich wollte Zeilen durchlaufen und die Datenfelder aus der XML-Spalte herausziehen und sie einfügen eine insert-Anweisung. Also 5 Zeilen, jeweils für 3 Datenspalten im XML-Feld = 15 Einfügungen, perfekt.
Dan Richardson

17

Dieser Beitrag war hilfreich, um mein Problem zu lösen, das ein etwas anderes XML-Format hat. Mein XML enthält eine Liste von Schlüsseln wie im folgenden Beispiel und ich speichere das XML in der SourceKeys-Spalte in einer Tabelle mit dem Namen DeleteBatch:

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

Erstellen Sie die Tabelle und füllen Sie sie mit einigen Daten:

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

Hier ist mein SQL, um die Schlüssel aus dem XML auszuwählen:

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

Hier sind die Abfrageergebnisse ...

ExecutionKey Key
1 1
1 2
1 3
2 100
2 101

9

Dies kann Ihre Frage beantworten:

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

Blimey. Dies war ein wirklich nützlicher Thread zu entdecken.

Ich fand einige dieser Vorschläge immer noch verwirrend. Wann immer ich valuemit [1]in der Zeichenfolge verwendet habe, wurde nur der erste Wert abgerufen. Und einige Vorschläge empfahlen, mit cross applydenen (in meinen Tests) einfach viel zu viele Daten zurückgebracht wurden.

Hier ist mein einfaches Beispiel, wie Sie ein xmlObjekt erstellen und dann seine Werte in eine Tabelle einlesen.

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)

Und hier ist die Ausgabe:

Geben Sie hier die Bildbeschreibung ein

Es ist eine bizarre Syntax, aber mit einem anständigen Beispiel ist es einfach genug, Ihre eigenen SQL Server-Funktionen zu erweitern.

Apropos, hier ist die richtige Antwort auf diese Frage.

Angenommen, Sie haben Ihre XML-Daten in einer @xmlVariablen vom Typ xml(wie in meinem obigen Beispiel gezeigt), so würden Sie die drei Datenzeilen aus der in der Frage angegebenen XML-Datei zurückgeben:

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)

Geben Sie hier die Bildbeschreibung ein


Ich sehe nicht, wie das die richtige Antwort ist. Das OP fordert Sie auf, eine Spalte aus einer Tabelle vom Typ XML abzufragen. In diesem Fall müssen Sie entweder [1]die Index-Ordnungszahl verwenden, um die Rückgabe einer Zeile zu erzwingen, oder Sie müssen die Spalte kreuzen nodes(), um eine zu erhalten Struktur, gegen die xpath ausgeführt werden kann. Ihr Code lässt sich ohne viele Änderungen nicht in dieses Szenario übersetzen. Sie verwenden eine Variable, keine Tabellenspalte. Sie verwenden auch eine query()Funktion, die XML zurückgibt. zB könnten Sie gerade habenx.Rec.value('(./firstName)[1]', 'nvarchar(2000)') AS FirstName
Davos

3

Wenn Sie in der Lage sind, Ihr XML in ein Stammelement zu verpacken, ist Folgendes die Lösung:

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)

Geben Sie hier die Bildbeschreibung ein


3

MSSQL verwendet reguläre XPath-Regeln wie folgt:

  • Knotenname Wählt alle Knoten mit dem Namen "Knotenname" aus.
  • / Wählt aus dem Wurzelknoten
  • // Wählt Knoten im Dokument aus dem aktuellen Knoten aus, die der Auswahl entsprechen, unabhängig davon, wo sie sich befinden
  • . Wählt den aktuellen Knoten aus
  • .. Wählt das übergeordnete Element des aktuellen Knotens aus
  • @ Wählt Attribute aus

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

/ * In diesem Beispiel wird eine XML-Variable mit einem Schema verwendet * /

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('/*')
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.