SQL Server의 테이블에서 XML 값 및 특성을 쿼리하는 방법은 무엇입니까?


89

Xml열 이 포함 된 테이블이 있습니다 .

SELECT * 
FROM Sqm

여기에 이미지 설명 입력

xml행 의 데이터 샘플은 다음 과 같습니다.

<Sqm version="1.2">
  <Metrics>
    <Metric id="TransactionCleanupThread.RecordUsedTransactionShift" type="timer" unit="µs" count="1" sum="21490"   average="21490"   minValue="73701"    maxValue="73701"                               >73701</Metric>
    <Metric id="TransactionCleanupThread.RefundOldTrans"             type="timer" unit="µs" count="1" sum="184487"  average="184487"  minValue="632704"   maxValue="632704"                              >632704</Metric>
    <Metric id="Database.CreateConnection_SaveContextUserGUID"       type="timer" unit="µs" count="2" sum="7562"    average="3781"    minValue="12928"    maxValue="13006"    standardDeviation="16"     >12967</Metric>
    <Metric id="Global.CurrentUser"                                  type="timer" unit="µs" count="6" sum="4022464" average="670411"  minValue="15"       maxValue="13794345" standardDeviation="1642047">2299194</Metric>
    <Metric id="Global.CurrentUser_FetchIdentityFromDatabase"        type="timer" unit="µs" count="1" sum="4010057" average="4010057" minValue="13752614" maxValue="13752614"                            >13752614</Metric>
  </Metrics>
</Sqm>

이 데이터의 경우 다음을 원합니다.

SqmId  id                                                   type   unit  count  sum      minValue  maxValue  standardDeviation  Value
=====  ===================================================  =====  ====  =====  ======   ========  ========  =================  ======
1      TransactionCleanupThread.RecordUsedTransactionShift  timer  µs    1      21490    73701     73701     NULL               73701
1      TransactionCleanupThread.RefundOldTrans              timer  µs    1      184487   632704    632704    NULL               632704
1      Database.CreateConnection_SaveContextUserGUID        timer  µs    2      7562     12928     13006     16                 12967
1      Global.CurrentUser                                   timer  µs    6      4022464  15        13794345  1642047            2299194
1      Global.CurrentUser_FetchIdentityFromDatabase         timer  µs    1      4010057  13752614  13752614  NULL               13752614
2      ...

결국 나는 실제로 수행됩니다 SUM(), MIN(), MAX()집계. 하지만 지금 은 xml 열 을 쿼리 하려고합니다 .

의사 코드에서는 다음과 같이 시도합니다.

SELECT
    SqmId,
    Data.query('/Sqm/Metrics/Metric/@id') AS id,
    Data.query('/Sqm/Metrics/Metric/@type') AS type,
    Data.query('/Sqm/Metrics/Metric/@unit') AS unit,
    Data.query('/Sqm/Metrics/Metric/@sum') AS sum,
    Data.query('/Sqm/Metrics/Metric/@count') AS count,
    Data.query('/Sqm/Metrics/Metric/@minValue') AS minValue,
    Data.query('/Sqm/Metrics/Metric/@maxValue') AS maxValue,
    Data.query('/Sqm/Metrics/Metric/@standardDeviation') AS standardDeviation,
    Data.query('/Sqm/Metrics/Metric') AS value
FROM Sqm

그러나 해당 SQL 쿼리는 작동하지 않습니다.

메시지 2396, 수준 16, 상태 1, 줄 2
XQuery [Sqm.data.query ()] : 특성이 요소 외부에 나타나지 않을 수 있습니다.

나는 사냥을 해왔고, Xml 쿼리가 얼마나 잘 문서화되지 않았는지, 또는 예제가되어 있는지 놀랍습니다. 대부분의 리소스는 테이블 을 쿼리하는 대신 변수를 쿼리 합니다 . 내가하지 않는. 대부분의 리소스는 값을 읽는 대신 필터링 및 선택을 위해 xml 쿼리 만 사용합니다. 대부분의 리소스는 실제 값이 아닌 하드 코딩 된 자식 노드 (인덱스 기준)를 읽습니다.

내가 읽은 관련 자료

업데이트 : .query가 아닌 .value

.value대신을 사용하여 무작위로 시도했습니다 .query.

SELECT
    Sqm.SqmId,
    Data.value('/Sqm/Metrics/Metric/@id', 'varchar(max)') AS id,
    Data.value('/Sqm/Metrics/Metric/@type', 'varchar(max)') AS type,
    Data.value('/Sqm/Metrics/Metric/@unit', 'varchar(max)') AS unit,
    Data.value('/Sqm/Metrics/Metric/@sum', 'varchar(max)') AS sum,
    Data.value('/Sqm/Metrics/Metric/@count', 'varchar(max)') AS count,
    Data.value('/Sqm/Metrics/Metric/@minValue', 'varchar(max)') AS minValue,
    Data.value('/Sqm/Metrics/Metric/@maxValue', 'varchar(max)') AS maxValue,
    Data.value('/Sqm/Metrics/Metric/@standardDeviation', 'varchar(max)') AS standardDeviation,
    Data.value('/Sqm/Metrics/Metric', 'varchar(max)') AS value
FROM Sqm

그러나 그것은 또한 작동하지 않습니다.

Msg 2389, Level 16, State 1, Line 3 XQuery [Sqm.data.value ()] :
'value ()'에는 싱글 톤 (또는 빈 시퀀스)이 필요하며 'xdt : untypedAtomic *'유형의 피연산자를 찾았습니다.

답변:


113

실제로 목표에 가깝습니다. nodes () 메서드를 사용 하여 행을 분할 한 다음 값을 가져 오면됩니다.

select
    s.SqmId,
    m.c.value('@id', 'varchar(max)') as id,
    m.c.value('@type', 'varchar(max)') as type,
    m.c.value('@unit', 'varchar(max)') as unit,
    m.c.value('@sum', 'varchar(max)') as [sum],
    m.c.value('@count', 'varchar(max)') as [count],
    m.c.value('@minValue', 'varchar(max)') as minValue,
    m.c.value('@maxValue', 'varchar(max)') as maxValue,
    m.c.value('.', 'nvarchar(max)') as Value,
    m.c.value('(text())[1]', 'nvarchar(max)') as Value2
from sqm as s
    outer apply s.data.nodes('Sqm/Metrics/Metric') as m(c)

sql fiddle demo


1
노드 자체 의 "값" 을 얻으려면 어떻게합니까 ? select m.*그것이 만든 비밀스럽고 마법적인 중간 테이블을 볼 방법이없는 것 같습니다 . 요소의 값을 쿼리하는 구문은 무엇입니까? 예를 들어,의 값은 <Metric>8675309</Metric>이다 "8675309"
이안 보이드

1
@IanBoyd 죄송합니다, 그것을 놓쳤습니다, 업데이트를 참조하십시오. 당신이 사용할 수있는 '.' 또는 중첩 된 요소가있을 수있는 경우 텍스트
Roman

2
이 쿼리에서 별칭 s, mc은 무엇을 나타 냅니까?
Ian R. O'Brien

3
IanR.O'Brien @가 m결과 집합에서 반환하는 nodes()함수 s이다 sqm테이블 자체는, c에 의해 반환되는 결과 집합에서 데이터 타입과 XML 열인 nodes()기능
로마 Pekar

11

나는 매우 유사한 것을 시도했지만 노드를 사용하지 않았습니다. 그러나 내 xml 구조는 약간 다릅니다.

다음과 같습니다.

<Metrics>
    <Metric id="TransactionCleanupThread.RefundOldTrans" type="timer" ...>

대신 다음과 같은 경우 :

<Metrics>
    <Metric>
        <id>TransactionCleanupThread.RefundOldTrans</id>
        <type>timer</type>
        .
        .
        .

그러면이 SQL 문을 간단히 사용할 수 있습니다.

SELECT
    Sqm.SqmId,
    Data.value('(/Sqm/Metrics/Metric/id)[1]', 'varchar(max)') as id,
    Data.value('(/Sqm/Metrics/Metric/type)[1]', 'varchar(max)') AS type,
    Data.value('(/Sqm/Metrics/Metric/unit)[1]', 'varchar(max)') AS unit,
    Data.value('(/Sqm/Metrics/Metric/sum)[1]', 'varchar(max)') AS sum,
    Data.value('(/Sqm/Metrics/Metric/count)[1]', 'varchar(max)') AS count,
    Data.value('(/Sqm/Metrics/Metric/minValue)[1]', 'varchar(max)') AS minValue,
    Data.value('(/Sqm/Metrics/Metric/maxValue)[1]', 'varchar(max)') AS maxValue,
    Data.value('(/Sqm/Metrics/Metric/stdDeviation)[1]', 'varchar(max)') AS stdDeviation,
FROM Sqm

나에게 이것은 외부 적용 또는 교차 적용을 사용하는 것보다 훨씬 덜 혼란 스럽습니다.

다른 사람이 더 간단한 솔루션을 찾는 데 도움이되기를 바랍니다.


1
코드에 여는 괄호가 없습니다. 또한 추가 /text()성능 향상을위한 ID 등 후
대니 목장 주인에게

이것은 가장 격렬합니다. 감사합니다, 완벽하게 일했습니다.
SE

이 접근 방식으로 XML 유형 열이있는 테이블을 어떻게 쿼리합니까? 감사합니다.
FMFF

10

value대신 사용 query(XQuery에서 반환 할 노드의 인덱스를 지정하고 두 번째 매개 변수로 반환 할 SQL 데이터 형식을 전달해야 함) :

select
    xt.Id
    , x.m.value( '@id[1]', 'varchar(max)' ) MetricId
from
    XmlTest xt
    cross apply xt.XmlData.nodes( '/Sqm/Metrics/Metric' ) x(m)

8

일부 사람들이 XML을 값 테이블로 사용 cross apply하거나 outer apply변환하도록 제안하는 이유를 이해하지 못합니다 . 나에게 그것은 너무 많은 데이터를 가져 왔습니다.

다음은 xml개체를 만든 다음이를 테이블로 바꾸는 방법에 대한 제 예입니다 .

(나는 쉽게 읽을 수 있도록 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)

다음은 출력입니다.

여기에 이미지 설명 입력


궁금해 ... 왜 Varbinary(max)xml 캐스트 이전에 중첩 된 캐스트가 필요 합니까?
EvilDr

이 접근 방식으로 XML 유형 열이있는 테이블을 어떻게 쿼리합니까? 감사합니다.
FMFF

@FMFF이 방법은 실제로 교차 적용을 사용하는 다른 모든 방법과 동일합니다.
SgtWilko
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.