Làm cách nào để truy vấn các giá trị và thuộc tính Xml từ bảng trong SQL Server?


88

Tôi có một bảng chứa một Xmlcột:

SELECT * 
FROM Sqm

nhập mô tả hình ảnh ở đây

Một mẫu xmldữ liệu của một hàng sẽ là:

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

Trong trường hợp của dữ liệu này, tôi muốn:

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

Cuối cùng tôi thực sự sẽ được thực hiện SUM(), MIN(), MAX()tập hợp. Nhưng bây giờ tôi chỉ đang cố gắng truy vấn một cột xml.

Trong mã giả, tôi sẽ thử một cái gì đó như:

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

Nhưng truy vấn SQL đó không hoạt động:

Msg 2396, Cấp 16, Trạng thái 1, Dòng 2
XQuery [Sqm.data.query ()]: Thuộc tính có thể không xuất hiện bên ngoài một phần tử

Tôi đã săn lùng, và thật ngạc nhiên là cách truy vấn Xml được ghi chép lại kém tài liệu hoặc được lấy mẫu. Hầu hết các tài nguyên thay vì truy vấn một bảng , hãy truy vấn một biến ; mà tôi không làm. Hầu hết các tài nguyên chỉ sử dụng truy vấn xml để lọc và lựa chọn, thay vì đọc các giá trị. Hầu hết các tài nguyên đọc các nút con được mã hóa cứng (theo chỉ mục), thay vì các giá trị thực tế.

Các tài nguyên liên quan mà tôi đã đọc

Cập nhật: .value thay vì .query

Tôi đã thử sử dụng ngẫu nhiên .value, thay cho .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

Nhưng điều đó cũng không hoạt động:

Msg 2389, Mức 16, Trạng thái 1, Dòng 3 XQuery [Sqm.data.value ()]:
'value ()' yêu cầu một singleton (hoặc chuỗi trống), toán hạng tìm thấy thuộc loại 'xdt: untypedAtomic *'

Câu trả lời:


113

Trên thực tế, bạn đã gần đạt được mục tiêu của mình, bạn chỉ cần sử dụng phương thức node () để tách các hàng của mình và sau đó nhận các giá trị:

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
Làm cách nào để lấy "giá trị" của chính nút? Dường như không có cách nào để select m.*nhìn thấy bảng trung gian bí mật, ma thuật mà nó đã xây dựng. Cú pháp để truy vấn giá trị của một phần tử là gì? ví dụ như giá trị của <Metric>8675309</Metric>"8675309"
Ian Boyd

1
@IanBoyd xin lỗi, bỏ lỡ điều đó, xem cập nhật. Bạn có thể dùng '.' hoặc văn bản nếu có thể có yếu tố lồng nhau
La Mã Pekar

2
Những gì các bí danh s, mcđại diện trong truy vấn này?
Ian R. O'Brien

3
@ IanR.O'Brien mđược resultset trả về bởi nodes()hàm, ssqmbảng chính nó, clà cột với kiểu dữ liệu xml trong resultset trả về bởi nodes()chức năng
La Mã Pekar

11

Tôi đã cố gắng làm điều gì đó tương tự nhưng không sử dụng các nút. Tuy nhiên, cấu trúc xml của tôi hơi khác một chút.

Bạn có nó như thế này:

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

Nếu nó giống như thế này thay vào đó:

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

Sau đó, bạn có thể chỉ cần sử dụng câu lệnh SQL này.

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

Đối với tôi, điều này ít gây nhầm lẫn hơn nhiều so với việc sử dụng bôi ngoài hoặc bôi chéo.

Tôi hy vọng điều này sẽ giúp ai đó khác đang tìm kiếm một giải pháp đơn giản hơn!


1
mã không có dấu ngoặc mở. cũng gắn /text()sau id vv để tăng hiệu suất
Danny Rancher

Đây là điều dễ hiểu nhất. Cảm ơn, đã làm việc hoàn hảo.
ĐN

Làm cách nào để chúng tôi truy vấn một bảng có cột kiểu XML với cách tiếp cận này? Cảm ơn bạn.
FMFF

10

sử dụng valuethay vì query(phải chỉ định chỉ mục của nút để trả về trong XQuery cũng như chuyển kiểu dữ liệu sql để trả về dưới dạng tham số thứ hai):

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

Tôi không hiểu tại sao một số người đề xuất sử dụng cross applyhoặc outer applychuyển đổi xml thành một bảng giá trị. Đối với tôi, điều đó chỉ mang lại quá nhiều dữ liệu.

Đây là ví dụ của tôi về cách bạn tạo một xmlđối tượng, sau đó biến nó thành một bảng.

(Tôi đã thêm khoảng trắng trong chuỗi xml của mình, chỉ để giúp dễ đọc hơn.)

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)

Và đây là đầu ra:

nhập mô tả hình ảnh ở đây


Tò mò ... tại sao dàn diễn viên lồng nhau Varbinary(max)trước dàn diễn viên xml?
EvilDr

Làm cách nào để chúng tôi truy vấn một bảng có cột kiểu XML với cách tiếp cận này? Cảm ơn bạn.
FMFF
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.