Concatena tutti i valori dello stesso elemento XML usando XPath / XQuery


14

Ho un valore XML come questo:

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

Voglio per concatenare tutti i Ivalori e restituirli come una singola stringa: ABC....

Ora so che posso distruggere l'XML, aggregare i risultati come XML senza nome e applicare .values('text()[1]', ...)al risultato:

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

Tuttavia, vorrei fare tutto ciò utilizzando solo i metodi XPath / XQuery, qualcosa del genere:

SELECT @MyXml. ? ( ? );

C'è un modo simile?

Il motivo per cui sto cercando una soluzione in questa direzione è perché il mio XML reale contiene anche altri elementi, ad esempio:

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

E vorrei essere in grado di estrarre sia i Ivalori come singola stringa che i Jvalori come singola stringa senza dover utilizzare uno script ingombrante per ciascuno.

Risposte:


11

Questo potrebbe funzionare per te:

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

Raccoglie tutti gli text()elementi dal primo Re dal basso.

Se vuoi solo tutto quello text()che puoi fare

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

Se si desidera i valori per Ie Jseparati, fare questo invece.

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

L'ultimo mi è stato suggerito in chat, ma trovo anche il primo estremamente utile. Potrei essere in grado di generare i dati XML in modo diverso in modo da poter applicare il primo metodo ad esso.
Andriy M,

7

A seconda della struttura XML effettiva, potresti prendere in considerazione l'utilizzo di un ciclo come questo:

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

che produce questo:

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

Vedi questo violino


1
Questo è veramente buono. Posso facilmente adattarlo per includere i delimitatori ogni volta che ne ho bisogno. E non è troppo prolisso per essere usato come è nel caso in cui voglio estrarre stringhe con e senza delimitatori in modo uniforme.
Andriy M,

0

Se i tuoi elementi e valori sono davvero brevi e distinti, questo funziona:

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

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

Tuttavia, per XML non banali potrebbe essere difficile.


Gli elementi possono essere brevi, ma i valori in generale non lo sono e non posso essere sicuro che non contengano gli stessi caratteri dei nomi degli elementi. Apprezzo l'approccio fuori dagli schemi, però.
Andriy M
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.