Truy vấn SQL để nối các giá trị cột từ nhiều hàng trong Oracle


169

Có thể xây dựng SQL để nối các giá trị cột từ nhiều hàng không?

Sau đây là một ví dụ:

Bảng A

PID
Một
B
C

Bảng B

PID SEQ Desc

1 có
2 tốt
3 ngày.
B 1 Công việc tốt.
C 1 Có
C 2 chúng ta có thể 
C 3 làm 
C 4 công việc này!

Đầu ra của SQL phải là -

Mã hóa
A Chúc một ngày tốt lành.
B Làm việc tốt.
C Có, chúng tôi có thể làm công việc này!

Vì vậy, về cơ bản, cột Desc cho bảng đặt ra là sự kết hợp của các giá trị SEQ từ Bảng B?

Bất kỳ trợ giúp với SQL?



Hãy nhìn vào giải pháp này . Nó sẽ hữu ích cho bạn.
Jineesh Uaugeavida

Câu trả lời:


237

Có một số cách tùy thuộc vào phiên bản bạn có - xem tài liệu tiên tri về kỹ thuật tổng hợp chuỗi . Một cái rất phổ biến là sử dụng LISTAGG:

SELECT pid, LISTAGG(Desc, ' ') WITHIN GROUP (ORDER BY seq) AS description
FROM B GROUP BY pid;

Sau đó tham gia để Achọn ra những pidsgì bạn muốn.

Lưu ý: Ra khỏi hộp, LISTAGGchỉ hoạt động chính xác với VARCHAR2các cột.


2
sử dụng wm_concat () cho Oracle 10g ghép văn bản theo thứ tự tăng dần của số thứ tự được phân định bằng dấu phẩy, chúng ta có thể tạo giới hạn giảm dần bởi một thứ khác không?
jagamot

19

Ngoài ra còn có một XMLAGGchức năng, hoạt động trên các phiên bản trước ngày 11.2. Bởi vì WM_CONCATđược cung cấp tài liệu và không được hỗ trợ bởi Oracle , nó được đề nghị không sử dụng nó trong hệ thống sản xuất.

Với XMLAGGbạn có thể làm như sau:

SELECT XMLAGG(XMLELEMENT(E,ename||',')).EXTRACT('//text()') "Result" 
FROM employee_names

Cái này làm gì

  • đặt các giá trị của enamecột (nối với dấu phẩy) từ employee_namesbảng trong phần tử xml (có thẻ E)
  • trích văn bản này
  • tổng hợp xml (nối nó)
  • gọi cột kết quả là "Kết quả"

XMLAGG hoạt động trên Oracle 12.2. Hơn nữa, XLMAGG cho phép nối các chuỗi rất dài mà LISTAGG có thể không phải do độ dài cuối cùng của chúng.
Marco

13

Với mệnh đề mô hình SQL:

SQL> select pid
  2       , ltrim(sentence) sentence
  3    from ( select pid
  4                , seq
  5                , sentence
  6             from b
  7            model
  8                  partition by (pid)
  9                  dimension by (seq)
 10                  measures (descr,cast(null as varchar2(100)) as sentence)
 11                  ( sentence[any] order by seq desc
 12                    = descr[cv()] || ' ' || sentence[cv()+1]
 13                  )
 14         )
 15   where seq = 1
 16  /

P SENTENCE
- ---------------------------------------------------------------------------
A Have a nice day
B Nice Work.
C Yes we can do this work!

3 rows selected.

Tôi đã viết về điều này ở đây . Và nếu bạn theo liên kết đến luồng OTN, bạn sẽ tìm thấy một số chi tiết, bao gồm so sánh hiệu suất.



8

Như hầu hết các câu trả lời cho thấy, LISTAGGlà lựa chọn rõ ràng. Tuy nhiên, một khía cạnh khó chịu LISTAGGlà nếu tổng chiều dài của chuỗi được nối vượt quá 4000 ký tự (giới hạn VARCHAR2trong SQL), lỗi dưới đây sẽ bị ném, rất khó để quản lý trong các phiên bản Oracle cho đến 12.1

ORA-01361: kết quả của chuỗi nối quá dài

Một tính năng mới được thêm vào trong 12cR2 là ON OVERFLOWmệnh đề của LISTAGG. Truy vấn bao gồm mệnh đề này sẽ như sau:

SELECT pid, LISTAGG(Desc, ' ' on overflow truncate) WITHIN GROUP (ORDER BY seq) AS desc
FROM B GROUP BY pid;

Ở trên sẽ hạn chế đầu ra tới 4000 ký tự nhưng sẽ không ném ORA-01489lỗi.

Đây là một số tùy chọn bổ sung của ON OVERFLOWmệnh đề:

  • ON OVERFLOW TRUNCATE 'Contd..' : Điều này sẽ hiển thị 'Contd..'ở cuối chuỗi (Mặc định là ...)
  • ON OVERFLOW TRUNCATE '' : Điều này sẽ hiển thị 4000 ký tự mà không có bất kỳ chuỗi kết thúc.
  • ON OVERFLOW TRUNCATE WITH COUNT: Điều này sẽ hiển thị tổng số ký tự ở cuối sau khi các ký tự kết thúc. Vd: - ' ...(5512)'
  • ON OVERFLOW ERROR: Nếu bạn dự đoán lỗi sẽ xảy ra LISTAGGvới ORA-01489lỗi (Dù sao đó cũng là mặc định).

6

Đối với những người phải giải quyết vấn đề này bằng cách sử dụng Oracle 9i (hoặc sớm hơn), có lẽ bạn sẽ cần sử dụng SYS_CONNECT_BY_PATH, vì LISTAGG không khả dụng.

Để trả lời OP, truy vấn sau đây sẽ hiển thị PID từ Bảng A và nối tất cả các cột DESC từ Bảng B:

SELECT pid, SUBSTR (MAX (SYS_CONNECT_BY_PATH (description, ', ')), 3) all_descriptions
FROM (
       SELECT ROW_NUMBER () OVER (PARTITION BY pid ORDER BY pid, seq) rnum, pid, description
       FROM (
              SELECT a.pid, seq, description
              FROM table_a a, table_b b
              WHERE a.pid = b.pid(+)
             )
      )
START WITH rnum = 1
CONNECT BY PRIOR rnum = rnum - 1 AND PRIOR pid = pid
GROUP BY pid
ORDER BY pid;

Cũng có thể có các trường hợp trong đó tất cả các khóa và giá trị được chứa trong một bảng. Có thể sử dụng truy vấn sau đây khi không có Bảng A và chỉ có Bảng B tồn tại:

SELECT pid, SUBSTR (MAX (SYS_CONNECT_BY_PATH (description, ', ')), 3) all_descriptions
FROM (
       SELECT ROW_NUMBER () OVER (PARTITION BY pid ORDER BY pid, seq) rnum, pid, description
       FROM (
              SELECT pid, seq, description
              FROM table_b
             )
      )
START WITH rnum = 1
CONNECT BY PRIOR rnum = rnum - 1 AND PRIOR pid = pid
GROUP BY pid
ORDER BY pid;

Tất cả các giá trị có thể được sắp xếp lại theo ý muốn. Các mô tả được nối riêng lẻ có thể được sắp xếp lại trong mệnh đề PHẦN TỪ B BYNG và danh sách các PID có thể được sắp xếp lại trong mệnh đề ORDER BY cuối cùng.


Luân phiên: có thể đôi khi bạn muốn nối tất cả các giá trị từ toàn bộ một bảng thành một hàng.

Ý tưởng chính ở đây là sử dụng một giá trị nhân tạo cho nhóm mô tả được nối.

Trong truy vấn sau, chuỗi hằng số '1' được sử dụng, nhưng mọi giá trị sẽ hoạt động:

SELECT SUBSTR (MAX (SYS_CONNECT_BY_PATH (description, ', ')), 3) all_descriptions
FROM (
       SELECT ROW_NUMBER () OVER (PARTITION BY unique_id ORDER BY pid, seq) rnum, description
       FROM (
              SELECT '1' unique_id, b.pid, b.seq, b.description
              FROM table_b b
             )
      )
START WITH rnum = 1
CONNECT BY PRIOR rnum = rnum - 1;

Các mô tả được nối riêng lẻ có thể được sắp xếp lại trong mệnh đề PHẦN THAM GIA.

Một số câu trả lời khác trên trang này cũng đã đề cập đến tài liệu tham khảo cực kỳ hữu ích này: https://oracle-base.com/articles/misc/opes-aggregation-t kỹ thuật


3
  1. LISTAGG mang lại hiệu suất tốt nhất nếu việc sắp xếp là bắt buộc (00: 00: 05,85)

    SELECT pid, LISTAGG(Desc, ' ') WITHIN GROUP (ORDER BY seq) AS description FROM B GROUP BY pid;

  2. COLLECT mang lại hiệu suất tốt nhất nếu không cần sắp xếp (00: 00: 02.90):

    SELECT pid, TO_STRING(CAST(COLLECT(Desc) AS varchar2_ntt)) AS Vals FROM B GROUP BY pid;

  3. THU THẬP với thứ tự chậm hơn một chút (00: 00: 07.08):

    SELECT pid, TO_STRING(CAST(COLLECT(Desc ORDER BY Desc) AS varchar2_ntt)) AS Vals FROM B GROUP BY pid;

Tất cả các kỹ thuật khác đã chậm hơn.


1
Nó sẽ hữu ích để giải thích về câu trả lời của bạn.
Jon Surrell

John, tôi không muốn nhắc lại từ bài viết nhưng tóm lại đây là những kết quả: 1. LISTAGG mang lại hiệu suất tốt nhất nếu việc sắp xếp là bắt buộc (00: 00: 05,85) 2. THU THẬP mang lại hiệu suất tốt nhất nếu không sắp xếp cần thiết (00: 00: 02.90): CHỌN pid, TO_STRING (CAST (THU THẬP (Desc) AS varchar2_ntt)) NHƯ Vals TỪ B GROUP BY pid; 3. THU THẬP với thứ tự chậm hơn một chút (00: 00: 07,08): CHỌN pid, TO_STRING (CAST (THU THẬP (Desc ORDER BY Desc) AS varchar2_ntt)) NHƯ Vals TỪ B GROUP BY pid; Tất cả các kỹ thuật khác đã chậm hơn.
Misho

1
Bạn chỉ có thể chỉnh sửa câu trả lời của bạn để bao gồm thông tin liên quan.
Jon Surrell

Tôi đã quá muộn trong việc chỉnh sửa và đó là lý do tại sao tôi lại thêm nó vào. Xin lỗi tôi là người mới ở đây và mới bắt đầu hiểu rõ.
Misho

1

Trước khi bạn chạy một truy vấn chọn, hãy chạy này:

SET SERVEROUT ON SIZE 6000

SELECT XMLAGG(XMLELEMENT(E,SUPLR_SUPLR_ID||',')).EXTRACT('//text()') "SUPPLIER" 
FROM SUPPLIERS;


-3

Trong phần chọn nơi bạn muốn nối, hãy gọi hàm SQL.

Ví dụ:

select PID, dbo.MyConcat(PID)
   from TableA;

Sau đó, cho chức năng SQL:

Function MyConcat(@PID varchar(10))
returns varchar(1000)
as
begin

declare @x varchar(1000);

select @x = isnull(@x +',', @x, @x +',') + Desc
  from TableB
    where PID = @PID;

return @x;

end

Cú pháp tiêu đề hàm có thể sai, nhưng nguyên tắc không hoạt động.


Điều này không hợp lệ đối với Oracle
a_horse_with_no_name
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.