Verketten Sie alle Werte desselben XML-Elements mit XPath / XQuery

14

Ich habe einen XML-Wert wie diesen:

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

Ich möchte alle verketten IWerte und bringt sie als einzelne Zeichenfolge: ABC....

Jetzt weiß ich, dass ich das XML zerkleinern, die Ergebnisse als knotenloses XML zusammenfassen und .values('text()[1]', ...)auf das Ergebnis anwenden kann :

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

All dies möchte ich jedoch nur mit XPath / XQuery-Methoden tun, etwa so:

SELECT @MyXml. ? ( ? );

Gibt es so etwas?

Der Grund, warum ich nach einer Lösung in diese Richtung suche, ist, dass mein tatsächliches XML auch andere Elemente enthält, zum Beispiel:

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

Und ich möchte in der Lage sein, sowohl die IWerte als einzelne Zeichenfolge als auch die JWerte als einzelne Zeichenfolge zu extrahieren, ohne für jedes ein unhandliches Skript verwenden zu müssen.

Andriy M
quelle

Antworten:

11

Das könnte für Sie funktionieren:

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

Es nimmt alle text()Elemente von der ersten Rbis zur unteren auf.

Wenn Sie nur alles wollen, was text()Sie tun können

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

Wenn Sie die Werte für Iund Jseparat möchten, tun Sie dies stattdessen.

select @MyXml.query('/R/I/text()').value('.', 'varchar(50)'),
       @MyXml.query('/R/J/text()').value('.', 'varchar(50)')
Mikael Eriksson
quelle
Der letzte wurde mir im Chat vorgeschlagen, aber ich finde den ersten auch sehr hilfreich. Möglicherweise kann ich die XML-Daten anders generieren, sodass ich die erste Methode darauf anwenden kann.
Andriy M
7

Abhängig von Ihrer tatsächlichen XML-Struktur können Sie eine Schleife wie die folgende verwenden:

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

was gibt dies aus:

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

Sehen Sie diese Geige

Tom V - Team Monica
quelle
1
Das ist sehr gut. Ich kann es leicht anpassen, um Begrenzer einzuschließen, wann immer ich muss. Und es ist nicht zu ausführlich, um verwendet zu werden, da ich Strings mit und ohne Trennzeichen auf einheitliche Weise extrahieren möchte.
Andriy M
0

Wenn Ihre Elemente und Werte wirklich kurz und eindeutig sind, funktioniert Folgendes:

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

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

Für nicht-triviales XML kann es jedoch schwierig sein.

Michael Green
quelle
Die Elemente können kurz sein, aber die Werte im Allgemeinen nicht, und ich kann nicht sicher sein, dass sie nicht dieselben Zeichen enthalten wie die Elementnamen. Schätzen Sie jedoch den Ansatz außerhalb des Rahmens.
Andriy M