จะสอบถามค่า Xml และแอตทริบิวต์จากตารางใน SQL Server ได้อย่างไร


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 สำหรับการกรองและการเลือกแทนที่จะอ่านค่า ทรัพยากรส่วนใหญ่อ่านโหนดชายด์แบบฮาร์ดโค้ด (ตามดัชนี) แทนที่จะเป็นค่าจริง

แหล่งข้อมูลที่เกี่ยวข้องที่ฉันอ่าน

อัปเดต: .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

แต่ก็ไม่ได้ผล:

ข่าวสารเกี่ยวกับ 2389 ระดับ 16 สถานะ 1 บรรทัด 3 XQuery [Sqm.data.value ()]:
'value ()' ต้องการซิงเกิลตัน (หรือลำดับว่าง) พบตัวถูกดำเนินการประเภท 'xdt: untypedAtomic *'

คำตอบ:


114

จริงๆแล้วคุณใกล้จะบรรลุเป้าหมายแล้วคุณเพียงแค่ใช้วิธีการโหนด ()เพื่อแบ่งแถวของคุณแล้วรับค่า:

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"
Ian Boyd

1
@IanBoyd ขออภัยพลาดที่ดูการอัปเดต คุณสามารถใช้ได้ '.' หรือข้อความหากมีองค์ประกอบที่ซ้อนกันได้
Roman Pekar

2
สิ่งใดที่นามแฝงs, mและcเป็นตัวแทนในแบบสอบถามนี้หรือไม่?
Ian R. O'Brien

3
@ IanR.O'Brien mเป็นผลลัพธ์ที่ส่งคืนโดยnodes()ฟังก์ชันsเป็นsqmตารางcเป็นคอลัมน์ที่มีชนิดข้อมูล xml ในชุดผลลัพธ์ที่ส่งคืนโดยnodes()ฟังก์ชัน
Roman 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 ฯลฯ เพื่อเพิ่มประสิทธิภาพ
Danny Rancher

นี่คือตรงไปตรงมาที่สุด ขอบคุณทำงานได้อย่างสมบูรณ์แบบ
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)

9

ฉันไม่เข้าใจว่าทำไมบางคนถึงแนะนำให้ใช้cross applyหรือouter applyแปลง xml เป็นตารางค่า สำหรับฉันนั่นทำให้ข้อมูลกลับมามากเกินไป

นี่คือตัวอย่างของฉันเกี่ยวกับวิธีสร้าง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 วิธีนี้เหมือนกับวิธีอื่น ๆ ทั้งหมดโดยใช้ cross apply
SgtWilko
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.