Ghép nối tất cả các giá trị của cùng một phần tử XML bằng XPath / XQuery


14

Tôi có một giá trị XML như thế này:

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

Tôi muốn nối tất cả các Igiá trị và trả về chúng dưới dạng một chuỗi : ABC....

Bây giờ tôi biết rằng tôi có thể hủy bỏ XML, tổng hợp lại các kết quả dưới dạng XML không gật đầu và áp dụng .values('text()[1]', ...)cho kết quả:

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

Tuy nhiên, tôi chỉ muốn làm tất cả những điều đó bằng cách sử dụng các phương thức XPath / XQuery, đại loại như thế này:

SELECT @MyXml. ? ( ? );

Có cách nào như vậy không?

Lý do tôi đang tìm kiếm một giải pháp theo hướng này là vì XML thực tế của tôi cũng chứa các yếu tố khác:

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

Và tôi muốn có thể trích xuất cả hai Igiá trị dưới dạng một chuỗi và các Jgiá trị dưới dạng một chuỗi mà không phải sử dụng tập lệnh khó sử dụng cho mỗi giá trị.

Câu trả lời:


11

Điều này có thể làm việc cho bạn:

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

Nó chọn tất cả các text()yếu tố từ đầu tiên Rvà bên dưới.

Nếu bạn chỉ muốn tất cả những text()gì bạn có thể làm

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

Nếu bạn muốn các giá trị cho IJriêng biệt làm điều này thay vào đó.

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

Người cuối cùng được gợi ý cho tôi trong trò chuyện nhưng tôi cũng thấy người đầu tiên cực kỳ hữu ích. Tôi có thể tạo dữ liệu XML khác nhau để tôi có thể áp dụng phương thức đầu tiên cho nó.
Andriy M

7

Tùy thuộc vào cấu trúc XML thực tế của bạn, bạn có thể xem xét sử dụng một vòng lặp như thế này:

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

cái nào tạo ra điều này:

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

Xem câu đố này


1
Cái này thật sự rất tốt. Tôi có thể dễ dàng điều chỉnh nó để bao gồm các dấu phân cách bất cứ khi nào tôi cần. Và nó không quá dài dòng để được sử dụng như trong trường hợp tôi muốn trích xuất các chuỗi cả có và không có dấu phân cách một cách thống nhất.
Andriy M

0

Nếu các yếu tố và giá trị của bạn thực sự ngắn và khác biệt thì công việc này:

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

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

Đối với XML không tầm thường, nó có thể đấu tranh, mặc dù.


Các phần tử có thể ngắn, nhưng các giá trị nói chung thì không, và tôi không thể chắc chắn chúng sẽ không chứa các ký tự giống như tên các phần tử. Mặc dù vậy, đánh giá cao cách tiếp cận bên ngoài hộp.
Andriy M
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.