web-dev-qa-db-ja.com

XPath / XQueryを使用して同じXML要素のすべての値を連結する

次のような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の値を1つの文字列として抽出することも、Jの値を1つの文字列として抽出することもできます。それぞれに扱いにくいスクリプトを使用する必要はありません。

14
Andriy M

これはあなたのために働くかもしれません:

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

最初のR以下からすべてのtext()要素を取得します。

すべてが必要な場合はtext()を実行できます

_select @MyXml.value('.', 'varchar(50)')
_

IJの値を別々にしたい場合は、代わりにこれを行ってください。

_select @MyXml.query('/R/I/text()').value('.', 'varchar(50)'),
       @MyXml.query('/R/J/text()').value('.', 'varchar(50)')
_
11
Mikael Eriksson

実際の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 

これを見てください フィドル

要素と値が本当に短くてはっきりしている場合、これはうまくいきます:

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

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

しかし、自明ではないXMLの場合は、困難な場合があります。

0
Michael Green