Menggabungkan semua nilai elemen XML yang sama menggunakan XPath / XQuery


14

Saya memiliki nilai XML seperti ini:

<R>
  <I>A</I>
  <I>B</I>
  <I>C</I>
  ...
</R>

Saya ingin menyatukan semua Inilai dan mengembalikan mereka sebagai string tunggal: ABC....

Sekarang saya tahu bahwa saya bisa merobek XML, mengumpulkan hasilnya kembali sebagai XML tanpa tanda, dan berlaku .values('text()[1]', ...)untuk hasilnya:

SELECT
  (
    SELECT
      n.n.value('text()[1]', 'varchar(50)') AS [text()]
    FROM
      @MyXml.nodes('/R/I') AS n (n)
    FOR XML
      PATH (''),
      TYPE
  ).value('text()[1]', 'varchar(50)')
;

Namun, saya ingin melakukan semua itu menggunakan metode XPath / XQuery saja, seperti ini:

SELECT @MyXml. ? ( ? );

Apakah ada cara seperti itu?

Alasan saya mencari solusi ke arah ini adalah karena XML saya yang sebenarnya mengandung elemen lain juga, misalnya:

<R>
  <I>A</I>
  <I>B</I>
  <I>C</I>
  ...
  <J>X</J>
  <J>Y</J>
  <J>Z</J>
  ...
</R>

Dan saya ingin dapat mengekstraksi Inilai - nilai sebagai string tunggal dan Jnilai - nilai sebagai string tunggal tanpa harus menggunakan skrip yang berat untuk masing-masing.

Jawaban:


11

Ini mungkin bekerja untuk Anda:

select @MyXml.value('/R[1]', 'varchar(50)')

Itu mengambil semua text()elemen dari yang pertama Rdan di bawah.

Jika Anda hanya ingin semua text()yang dapat Anda lakukan

select @MyXml.value('.', 'varchar(50)')

Jika Anda ingin nilai Idan Jpisahkan lakukan ini sebagai gantinya.

select @MyXml.query('/R/I/text()').value('.', 'varchar(50)'),
       @MyXml.query('/R/J/text()').value('.', 'varchar(50)')

Yang terakhir disarankan kepada saya dalam obrolan tetapi saya menemukan yang pertama juga sangat membantu. Saya mungkin dapat menghasilkan data XML secara berbeda sehingga saya dapat menerapkan metode pertama untuk itu.
Andriy M

7

Bergantung pada struktur XML Anda yang sebenarnya, Anda dapat mempertimbangkan untuk menggunakan loop seperti ini:

DECLARE @xml XML

SELECT @xml = '<R>
  <I>A</I>
  <I>B</I>
  <I>C</I>
  <J>X</J>
  <J>Y</J>
  <J>Z</J>
</R>'

SELECT 
    Tbl.Col.query('for $i in I return $i').value('.', 'nvarchar(max)'),
    Tbl.Col.query('for $i in J return $i').value('.', 'nvarchar(max)')
FROM @xml.nodes('R') Tbl(Col);

yang menghasilkan ini:

(No column name) | (No column name) 
---------------  | --------------- 
ABC              | XYZ 

Lihat biola ini


1
Ini sangat bagus. Saya dapat dengan mudah menyesuaikannya untuk menyertakan pembatas kapan pun saya perlu. Dan itu tidak terlalu verbose untuk digunakan karena kalau-kalau saya ingin mengekstraksi string baik dengan dan tanpa pembatas dengan cara yang seragam.
Andriy M

0

Jika elemen dan nilai Anda benar-benar singkat dan berbeda, ini berfungsi:

declare @s varchar(99) = '<R><I>A</I><I>B</I><I>C</I></R>';

select
    @s,
    REPLACE(TRANSLATE ( @s, '<>I/R', '     '), ' ', '');

Untuk XML non-sepele itu mungkin berjuang.


Elemen bisa pendek, tetapi nilai-nilai secara umum tidak, dan saya tidak yakin mereka tidak akan berisi karakter yang sama seperti nama elemen. Menghargai pendekatan luar kotak, meskipun.
Andriy M
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.