SQL Server'daki tablodan Xml değerleri ve nitelikleri nasıl sorgulanır?


89

XmlSütun içeren bir tablom var:

SELECT * 
FROM Sqm

görüntü açıklamasını buraya girin

Bir xmlsatırın verilerinin bir örneği şöyle olacaktır:

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

Bu veriler söz konusu olduğunda şunu isterim:

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

Sonunda Aslında gerçekleştiriyor olacağız SUM(), MIN(), MAX()agregasyonu. Ama şimdilik bir xml sütununu sorgulamaya çalışıyorum .

Sözde kodda şöyle bir şey denerdim:

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

Ancak bu SQL sorgusu çalışmıyor:

Msg 2396, Düzey 16, Durum 1, Satır 2
XQuery [Sqm.data.query ()]: Öznitelik bir öğenin dışında görünmeyebilir

Avlandım ve Xml sorgulamasının ne kadar kötü belgelenmiş veya örneklenmiş olması şaşırtıcı. Çoğu kaynak , bir tabloyu sorgulamak yerine , bir değişkeni sorgular ; ki ben yapmıyorum. Çoğu kaynak, değerleri okumak yerine yalnızca filtreleme ve seçim için xml sorgulamasını kullanır. Çoğu kaynak, gerçek değerler yerine sabit kodlanmış alt düğümleri (dizine göre) okur.

Okuduğum ilgili kaynaklar

Güncelle: .query yerine .value

Şunun .valueyerine rastgele kullanmayı denedim .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

Ama bu da işe yaramıyor:

Msg 2389, Düzey 16, Durum 1, Satır 3 XQuery [Sqm.data.value ()]:
'değer ()', 'xdt: untypedAtomic *' türünde işlenen bulunan bir tekli (veya boş sıra) gerektirir

Yanıtlar:


114

Aslında hedefinize yakınsınız , satırlarınızı bölmek ve ardından değerleri elde etmek için nodes () yöntemini kullanmanız yeterlidir :

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
Düğümün kendisinin "değerini" nasıl alırım ? select m.*Yaptığı gizli, büyülü, ara masayı görmenin bir yolu yok gibi görünüyor . Bir elemanın değerini sorgulamak için kullanılan sözdizimi nedir? örneğin değeri <Metric>8675309</Metric>olan "8675309"
Ian Boyd

1
@IanBoyd üzgünüm, bunu kaçırdım, güncellendi. Kullanabilirsiniz '.' veya iç içe öğeler varsa metin
Roman Pekar

2
Takma adları ne yok s, mve cbu sorguda temsil?
Ian R. O'Brien

3
IanR.O'Brien @ mresultset tarafından döndürülen nodes()fonksiyonu solan sqmtablosunun kendisi, cdönen resultset'de veri türü XML ile sütun nodes()fonksiyonu
Roma Pekar

11

Çok benzer bir şey yapmaya çalışıyorum ama düğümleri kullanmıyorum. Ancak benim xml yapım biraz farklı.

Buna sahipsin:

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

Bunun yerine böyle olsaydı:

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

O zaman bu SQL ifadesini kullanabilirsiniz.

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

Bana göre bu, dış uygulama veya çapraz uygulama kullanmaktan çok daha az kafa karıştırıcı.

Umarım bu, daha basit bir çözüm arayan başka birine yardımcı olur!


1
kod açılış parantezlerini kaçırıyor. /text()performans artışı için kimlik vb. arkasına da ekleyin
Danny Rancher 4'17

Bu en doğru olanıdır. Teşekkürler, mükemmel çalıştı.
SE

Bu yaklaşımla XML tipi sütun içeren bir tabloyu nasıl sorgulayabiliriz? Teşekkür ederim.
FMFF

10

valueyerine kullanın query(XQuery'de döndürülecek düğüm dizinini belirtmeli ve ayrıca ikinci parametre olarak döndürmek için sql veri türünü iletmelisiniz):

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

Bazı insanlar kullanılarak düşündüren neden anlamıyorum cross applyya outer applybir değerler tabloya xml dönüştürmek. Benim için bu çok fazla veriyi geri getirdi.

İşte bir xmlnesneyi nasıl yaratacağınıza ve sonra onu bir tabloya dönüştürebileceğinize dair örneğim.

(Okumayı kolaylaştırmak için xml dizime boşluklar ekledim.)

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)

Ve işte çıktı:

görüntü açıklamasını buraya girin


Merak ediyorum ... neden Varbinary(max)xml dökümünden önceki iç içe yerleştirme lütfen?
EvilDr

Bu yaklaşımla XML tipi sütun içeren bir tabloyu nasıl sorgulayabiliriz? Teşekkür ederim.
FMFF

@FMFF bu yöntem aslında cross apply kullanan tüm diğer yöntemlerle aynıdır.
SgtWilko
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.