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>

이것은 내 테이블에 세 개의 행입니다.

다음과 같이 SQL 결과를 테이블로 반환하고 싶습니다.

Jon  | Johnson
Kathy| Carter
Bob  | Burns

이 작업을 수행하는 쿼리는 무엇입니까?


xml의 ​​모든 요소를 ​​가져올 방법이 없습니까? 하나씩 지정해야하나요? 정말 지루해집니다. "select * from table"을 수행 할 수 있습니다. 원하는 모든 단일 요소를 지정하지 않고도 "select xml. * from xml"을 수행 할 수 있어야하는 것처럼 보입니다.
Keith Tyler

답변:


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

SQL Server 2008 R2 Express에서 솔루션과 함께 다음 오류를 반환했습니다. The XQuery syntax '/function()' is not supported.; 반면에 @Remus Rusanu는 :) 그것을 할 것 같다
RMiranda

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각 필드는 정확히 하나 개의 요소 '사람'이 포함 된 경우. XML이 선택한 변수 FROM @variable.nodes(...)이고 cross apply.


1
이 방법이 얼마나 효율적인지, 더 나은 방법이 있는지 궁금합니다. CROSS APPLY와 XPath 결과를 함께 사용하면 리소스를 많이 사용하는 쿼리가 발생할 수 있습니다.
redcalx 2010-06-21

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 을 참조하십시오 .
톰 Wayson

내가 필요로하는 것과 약간 다르지만 이것은 XML 열이있는 여러 행이있는 문제에 대한 완벽한 솔루션이었습니다. 행을 반복하고 XML 열 내에서 데이터 필드를 꺼내서 삽입하고 싶었습니다. 삽입 문. 따라서 XML 필드의 데이터 열 3 개당 5 개 행 = 15 개 삽입, 완벽합니다.
dan richardson

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
2100
2101

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

Blimey. 이것은 발견하기에 정말 유용한 스레드였습니다.

여전히 이러한 제안 중 일부가 혼란스러워졌습니다. 문자열에서 valuewith [1]를 사용할 때마다 첫 번째 값만 검색됩니다. 그리고 몇 가지 제안 cross apply은 (내 테스트에서) 너무 많은 데이터를 가져 오는 것을 사용 하는 것이 좋습니다 .

여기에 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에서 세 행의 데이터를 반환하는 방법입니다.

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)

여기에 이미지 설명 입력


이것이 어떻게 정답인지 모르겠습니다. 영업 이익은 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.