Об'єднайте всі значення одного елемента XML за допомогою XPath / XQuery


14

У мене є таке значення XML:

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

Я хочу , щоб об'єднати всі Iзначення і повертає їх у вигляді одного рядка: ABC....

Тепер я знаю, що я можу подрібнити XML, агрегувати результати назад як XML без вузла і застосувати .values('text()[1]', ...)до результату:

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)')
;

Однак я хотів би зробити все це, використовуючи лише методи XPath / XQuery, приблизно так:

SELECT @MyXml. ? ( ? );

Чи існує такий спосіб?

Я шукаю рішення в цьому напрямку, тому що мій фактичний XML містить і інші елементи, наприклад:

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

І я хотів би мати можливість витягувати як Iзначення в якості однієї рядка, так і Jзначення як окремий рядок, не використовуючи для кожного громіздкого сценарію.

Відповіді:


11

Це може допомогти вам:

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

Він підбирає всі text()елементи з першого Rта нижче.

Якщо ви просто хочете все, text()що можете зробити

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

Якщо ви хочете, щоб значення для Iі Jрозділилися, зробіть це замість цього.

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

Останній був запропонований мені в чаті, але я вважаю, що перший теж дуже корисний. Можливо, я зможу генерувати XML-дані по-різному, щоб я міг застосувати до нього перший метод.
Андрій М

7

Залежно від вашої фактичної структури XML, ви можете розглянути можливість використання циклу на зразок цього:

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

який виводить це:

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

Дивіться цю загадку


1
Це справді добре. Я можу легко адаптувати його, щоб включати роздільники, коли мені потрібно. І це не надто багатослівне використання, як на випадок, якщо я хочу витягувати рядки як з роздільниками, так і без них, рівномірним способом.
Андрій М

0

Якщо ваші елементи та цінності дійсно короткі і чіткі, це працює:

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

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

Однак для нетривіальної XML він може боротися.


Елементи можуть бути короткими, але значення в цілому ні, і я не можу бути впевнений, що вони не будуть містити таких же символів, як імена елементів. Хоча оцініть підхід поза коробкою.
Андрій М
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.