2009-05-22 19 views
92

sólo mirar a mi campo XML, mis filas se ven así:seleccione Valores de campo XML en SQL Server 2008

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

Tenga en cuenta que estos son tres filas en mi mesa.

me gustaría devolver un resultado como una tabla de SQL como en

Jon | Johnson 
Kathy| Carter 
Bob | Burns 

Lo que consulta será lograr esto?

Respuesta

134

Teniendo en cuenta que el campo XML se llama '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

Debe utilizar .nodes() y la cruz se aplica si xmlField contiene más de un elementos. –

+0

SQL Server 2008 R2 Express, me devolvió este error con su solución: 'La sintaxis de XQuery '/ función()' no es compatible. Por otro lado @Remus Rusanu parece hacerlo :) – RMiranda

+0

@RemusRusanu esta fue la clave para mí !!! ¡Gracias! – andrew

103

Teniendo en cuenta que los datos XML proviene de una mesa de 'mesa' y se almacena en un 'campo' columna: utilizar el XML methods, extracto valores con xml.value(), nodos del proyecto con xml.nodes(), utilice CROSS APPLY para unirse:

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) 

Usted puede deshacerse de la nodes() y cross apply si cada campo contiene exactamente un elemento 'persona'. Si el XML es una variable, seleccione FROM @variable.nodes(...) y no necesita el cross apply.

+1

Me pregunto qué tan eficiente es este método y si hay una mejor manera. La combinación CROSS APPLY con los resultados de XPath parece que podría dar lugar a una gran consulta de recursos. – redcalx

+1

@thelocster: esto no es diferente del acceso a datos ordinarios. Las técnicas para mejorar el rendimiento XML están bien documentadas. http://msdn.microsoft.com/en-us/library/ms345118%28SQL.90%29.aspx –

+2

tenga en cuenta que si su XML tiene espacios de nombres xmlns definidos, deberá definirlos en XQuery (XPath)) expresión arriba. Ver http://stackoverflow.com/a/1302150/656010 para un ejemplo. –

15

Este mensaje fue útil para resolver mi problema que tiene un pequeño formato XML diferente ... mi XML contiene una lista de claves como el siguiente ejemplo y almacenar el XML en la columna de la SourceKeys en una tabla llamada DeleteBatch:

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

Crear la tabla y rellenarla con algunos datos:

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

Aquí está mi SQL para seleccionar las teclas desde el XML:

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

aquí está el resultado de la consulta ...

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

Esto puede responder a su pregunta:

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 
1
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] 
3

Blimey. Este fue un hilo muy útil para descubrir.

Aún encuentro algunas de estas sugerencias confusas. Cada vez que usé value con [1] en la cadena, solo recuperaría el primer valor. Y algunas sugerencias recomiendan usar cross apply que (en mis pruebas) acaba de traer demasiados datos.

Así que, aquí está mi ejemplo simple de cómo crearías un objeto xml, luego leíste sus valores en una tabla.

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) 

Y aquí está la salida:

enter image description here

Es extraña sintaxis, pero con un ejemplo decente, es bastante fácil de añadir a sus propias funciones de SQL Server.

Hablando de eso, aquí está el correcto respuesta a esta pregunta.

Asumiendo que su tener sus datos XML en una variable @xml de tipo xml (como se demuestra en el ejemplo anterior), aquí es cómo se conducen los tres filas de datos del xml citado en la pregunta:

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) 

enter image description here

+0

No veo cómo esta es la respuesta correcta. El OP está pidiendo consultar una columna de una tabla que es de tipo XML, y en ese caso debe usar '[1]', el ordinal del índice para forzarlo a devolver 1 fila, o tiene que aplicar el método cruzado columna con 'nodes()' para obtener una estructura que pueda hacer que xpath se ejecute en su contra. Su código no se traduce en ese escenario sin muchas modificaciones. Está utilizando una variable, no una columna de tabla. También está sobreutilizando la función 'query()' que devuelve xml. p. podrías tener '' x.Rec.value ('(./ firstName) [1]', 'nvarchar (2000)') AS FirstName' – Davos

2

Si usted es capaz de envolver el código XML en un elemento raíz - dicen entonces el siguiente es su solución:

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) 

enter image description here

0

/* En este ejemplo se utiliza una variable XML con un esquema */

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('/*')