LISTAGG trong Oracle để trả về các giá trị riêng biệt


94

Tôi đang cố gắng sử dụng LISTAGGhàm trong Oracle. Tôi chỉ muốn nhận các giá trị riêng biệt cho cột đó. Có cách nào để tôi chỉ có thể nhận các giá trị riêng biệt mà không cần tạo một hàm hoặc một thủ tục không?

  col1 col2 Created_by
   1 2 Smith 
   1 2 John 
   1 3 Ajay 
   1 4 Ram 
   1 5 Jack 

Tôi cần chọn col1 và LISTAGGcol2 (cột 3 không được xem xét). Khi tôi làm điều đó, tôi nhận được một thứ như thế này là kết quả của LISTAGG: [2,2,3,4,5]

Tôi cần xóa '2' trùng lặp ở đây; Tôi chỉ cần các giá trị khác biệt của col2 so với col1.



Bạn có thể hiển thị (hàng) ouptut dự kiến ​​từ mẫu không? Bạn muốn biết điều gì nếu có nhiều hơn một giá trị cho col1?
a_horse_with_no_name

Đầu ra dự kiến ​​của LISTAGG là [2,3,4,5]. Số '2' thứ hai nên được loại bỏ. Và bảng của tôi có hơn 1000 hàng.
Priyanth

Bạn muốn biết điều gì nếu có nhiều hơn một giá trị cho col1?
a_horse_with_no_name

Đoạn mã như thế này: - CHỌN col1, LISTAGG (col2, ',') trong nhóm (thứ tự theo col2) TỪ bảng T WHERE .... Vì vậy, nó hiển thị tất cả các giá trị riêng biệt của col2 tương ứng với col1, được phân tách bởi dấu phẩy.
Priyanth

Câu trả lời:


77

19c trở lên:

select listagg(distinct the_column, ',') within group (order by the_column)
from the_table

18c trở về trước:

select listagg(the_column, ',') within group (order by the_column)
from (
   select distinct the_column 
   from the_table
) t

Nếu bạn cần nhiều cột hơn, một cái gì đó như thế này có thể là những gì bạn đang tìm kiếm:

select col1, listagg(col2, ',') within group (order by col2)
from (
  select col1, 
         col2,
         row_number() over (partition by col1, col2 order by col1) as rn
  from foo
  order by col1,col2
)
where rn = 1
group by col1;

2
Tương tự như những gì tôi đã nghĩ trong đầu. Nếu listagglà hàm tổng hợp duy nhất trong truy vấn, điều này nên làm. Tuy nhiên, việc kết hợp nó với các hàm tổng hợp khác sẽ khó hơn.
Andriy M,

Đúng. Truy vấn của tôi tương tự như thế này.
Priyanth

1
@a_horse_with_no_name: Câu lệnh select ở trên cung cấp các giá trị trùng lặp cho tôi. Tôi muốn xóa các bản sao. col1 col2 Tạo bởi 1 2 Smith 1 2 John 1 3 Ajay 1 4 Ram 1 5 Jack Tôi cần chọn col1 và LISTAGG của col2 (cột 3 không được xem xét). Trong khi tôi làm điều đó, tôi sẽ nhận được một cái gì đó như thế này với kết quả là od LISTAGG: -> [2,2,3,4,5] Tôi cần xóa bản sao'2 'ở đây Tôi chỉ cần các giá trị khác biệt của col2 so với col1 .
Priyanth

@a_horse_with_no_name: Tôi đã thử mã- và nhận được thông báo lỗi như bên dưới ORA-01489: kết quả của quá trình nối chuỗi quá dài 01489. 00000 - "kết quả của việc nối chuỗi quá dài" * Nguyên nhân: Kết quả nối chuỗi quá dài kích thước.
Priyanth

@Priyanth: thì bạn hết may rồi. Tổng chiều dài vượt quá 4000 byte và Oracle không thể xử lý điều đó. Bạn sẽ cần phải tổng hợp mã ứng dụng của mình.
a_horse_with_no_name

47

Đây là cách giải quyết vấn đề của bạn.

select  
      regexp_replace(
    '2,2,2.1,3,3,3,3,4,4' 
     ,'([^,]+)(,\1)*(,|$)', '\1\3')

from dual

trả lại

2,2.1,3,4

Từ oracle 19C, nó được xây dựng tại đây

Từ 18C trở lên thử trong nhóm xem tại đây

Nếu không, hãy sử dụng biểu thức chính quy

Trả lời dưới đây:

select col1, 

regexp_replace(
    listagg(
     col2 , ',') within group (order by col2)  -- sorted
    ,'([^,]+)(,\1)*(,|$)', '\1\3') )
   from tableX
where rn = 1
group by col1; 

Lưu ý: Cách trên sẽ hoạt động trong hầu hết các trường hợp - danh sách phải được sắp xếp, bạn có thể phải cắt bỏ tất cả khoảng trống ở cuối và đầu tùy thuộc vào dữ liệu của bạn.

Nếu bạn có rất nhiều mục trong một nhóm> 20 hoặc kích thước chuỗi lớn, bạn có thể gặp phải giới hạn kích thước chuỗi oracle 'kết quả của việc nối chuỗi quá dài'.

Từ oracle 12cR2 bạn có thể loại bỏ lỗi này xem tại đây . Hoặc đặt số lượng tối đa cho các thành viên trong mỗi nhóm. Điều này sẽ chỉ hoạt động nếu chỉ liệt kê những thành viên đầu tiên là được. Nếu bạn có chuỗi biến rất dài, điều này có thể không hoạt động. bạn sẽ phải thử nghiệm.

select col1,

case 
    when count(col2) < 100 then 
       regexp_replace(
        listagg(col2, ',') within group (order by col2)
        ,'([^,]+)(,\1)*(,|$)', '\1\3')

    else
    'Too many entries to list...'
end

from sometable
where rn = 1
group by col1;

Một giải pháp khác (không đơn giản như vậy) để hy vọng tránh giới hạn kích thước chuỗi oracle - kích thước chuỗi được giới hạn ở 4000. Cảm ơn bài đăng này ở đây của người dùng3465996

select col1  ,
    dbms_xmlgen.convert(  -- HTML decode
    dbms_lob.substr( -- limit size to 4000 chars
    ltrim( -- remove leading commas
    REGEXP_REPLACE(REPLACE(
         REPLACE(
           XMLAGG(
             XMLELEMENT("A",col2 )
               ORDER BY col2).getClobVal(),
             '<A>',','),
             '</A>',''),'([^,]+)(,\1)*(,|$)', '\1\3'),
                  ','), -- remove leading XML commas ltrim
                      4000,1) -- limit to 4000 string size
                      , 1)  -- HTML.decode
                       as col2
 from sometable
where rn = 1
group by col1;

V1 - một số trường hợp thử nghiệm - FYI

regexp_replace('2,2,2.1,3,3,4,4','([^,]+)(,\1)+', '\1')
-> 2.1,3,4 Fail
regexp_replace('2 ,2 ,2.1,3 ,3 ,4 ,4 ','([^,]+)(,\1)+', '\1')
-> 2 ,2.1,3,4 Success  - fixed length items

V2-các thẻ chứa trong các mục, ví dụ. 2,21

regexp_replace('2.1,1','([^,]+)(,\1)+', '\1')
-> 2.1 Fail
regexp_replace('2 ,2 ,2.1,1 ,3 ,4 ,4 ','(^|,)(.+)(,\2)+', '\1\2')
-> 2 ,2.1,1 ,3 ,4  -- success - NEW regex
 regexp_replace('a,b,b,b,b,c','(^|,)(.+)(,\2)+', '\1\2')
-> a,b,b,c fail!

v3 - regex cảm ơn Igor! hoạt động mọi trường hợp.

select  
regexp_replace('2,2,2.1,3,3,4,4','([^,]+)(,\1)*(,|$)', '\1\3') ,
---> 2,2.1,3,4 works
regexp_replace('2.1,1','([^,]+)(,\1)*(,|$)', '\1\3'),
--> 2.1,1 works
regexp_replace('a,b,b,b,b,c','([^,]+)(,\1)*(,|$)', '\1\3')
---> a,b,c works

from dual

3
Kết quả công bằng, nhưng không đơn giản như vậy. Với kích thước dữ liệu nghiêm trọng mà bạn sẽ gặp phải ORA-01489: result of string concatenation is too long.
Pero

1
Tôi sẽ không gọi nó là một giải pháp đơn giản nhưng rất hấp dẫn. Tôi không biết rằng số đối sánh có thể được sử dụng trong chuỗi tìm kiếm không chỉ chuỗi thay thế. Rực rỡ.
Peter Krassoi

1
Lưu ý, phương pháp này yêu cầu các giá trị phải được sắp xếp để các giá trị trùng lặp là liên tiếp. Nếu không, nó không thành công. Nhưng đơn giản là tốt! Và tôi đang sử dụng phương pháp này cho trường hợp cụ thể của tôi. Cảm ơn!
StewS2

2
siêu đơn giản không hoạt động trong hơn 3 lần lặp lại! , Ví dụ như a,b,b,b,b,csẽ trở thành a,b,b,c:-( (Oracle 11.2)
Andreas Dietrich

4
@AndreasDietrich - Các giải pháp sau đây có vẻ là luôn luôn đúng:regexp_replace(your_string, '([^,]+)(,\1)*(,|$)', '\1\3')
Egor Skriptunoff

10

bạn có thể sử dụng wm_concatchức năng không có giấy tờ .

select col1, wm_concat(distinct col2) col2_list 
from tab1
group by col1;

hàm này trả về cột clob, nếu bạn muốn, bạn có thể sử dụng dbms_lob.substrđể chuyển đổi clob thành varchar2.


15
Không, đừng sử dụng nó.
Koshinae

1
Đây chính xác là những gì tôi cần và hoạt động hoàn hảo trong truy vấn tổng hợp hiện có của tôi thay vì gói truy vấn đó trong một truy vấn bên ngoài. Có gì sai khi sử dụng wm_concat(distinct x)?
Ehryk

1
bởi vì nó không được ghi lại và không tồn tại trên 12c. nhưng dù sao trên các phiên bản cũ tôi nghĩ đó là cách tốt nhất.
Kemalettin Erbakırcı

1
Cảm ơn @ kemalettinerbakırcı! @thg bạn nên xem xét rằng nếu một cái gì đó không có tài liệu, bạn không biết đó là tác dụng phụ của nó là gì và bất kỳ loại nào khác mà Tài liệu cho bạn biết về các chức năng được ghi lại; bạn chỉ sử dụng nó như một hộp đen và bạn chỉ biết đòn bẩy nào hoạt động dựa trên văn hóa dân gian.
Koshinae

4
Không bao giờ sử dụng wm_concat. Xem Tại sao không sử dụng hàm WM_CONCAT trong Oracle? .
Lalit Kumar B

7

Tôi đã khắc phục vấn đề này bằng cách nhóm các giá trị trước, sau đó thực hiện một tổng hợp khác với listagg. Một cái gì đó như thế này:

select a,b,listagg(c,',') within group(order by c) c, avg(d)
from (select a,b,c,avg(d)
      from   table
      group by (a,b,c))
group by (a,b)

chỉ có một quyền truy cập toàn bộ bảng, tương đối dễ dàng để mở rộng sang các truy vấn phức tạp hơn


6

Nếu mục đích là áp dụng chuyển đổi này cho nhiều cột, tôi đã mở rộng giải pháp của a_horse_with_no_name:

SELECT * FROM
(SELECT LISTAGG(GRADE_LEVEL, ',') within group(order by GRADE_LEVEL) "Grade Levels" FROM (select distinct GRADE_LEVEL FROM Students) t)                     t1,
(SELECT LISTAGG(ENROLL_STATUS, ',') within group(order by ENROLL_STATUS) "Enrollment Status" FROM (select distinct ENROLL_STATUS FROM Students) t)          t2,
(SELECT LISTAGG(GENDER, ',') within group(order by GENDER) "Legal Gender Code" FROM (select distinct GENDER FROM Students) t)                               t3,
(SELECT LISTAGG(CITY, ',') within group(order by CITY) "City" FROM (select distinct CITY FROM Students) t)                                                  t4,
(SELECT LISTAGG(ENTRYCODE, ',') within group(order by ENTRYCODE) "Entry Code" FROM (select distinct ENTRYCODE FROM Students) t)                             t5,
(SELECT LISTAGG(EXITCODE, ',') within group(order by EXITCODE) "Exit Code" FROM (select distinct EXITCODE FROM Students) t)                                 t6,
(SELECT LISTAGG(LUNCHSTATUS, ',') within group(order by LUNCHSTATUS) "Lunch Status" FROM (select distinct LUNCHSTATUS FROM Students) t)                     t7,
(SELECT LISTAGG(ETHNICITY, ',') within group(order by ETHNICITY) "Race Code" FROM (select distinct ETHNICITY FROM Students) t)                              t8,
(SELECT LISTAGG(CLASSOF, ',') within group(order by CLASSOF) "Expected Graduation Year" FROM (select distinct CLASSOF FROM Students) t)                     t9,
(SELECT LISTAGG(TRACK, ',') within group(order by TRACK) "Track Code" FROM (select distinct TRACK FROM Students) t)                                         t10,
(SELECT LISTAGG(GRADREQSETID, ',') within group(order by GRADREQSETID) "Graduation ID" FROM (select distinct GRADREQSETID FROM Students) t)                 t11,
(SELECT LISTAGG(ENROLLMENT_SCHOOLID, ',') within group(order by ENROLLMENT_SCHOOLID) "School Key" FROM (select distinct ENROLLMENT_SCHOOLID FROM Students) t)       t12,
(SELECT LISTAGG(FEDETHNICITY, ',') within group(order by FEDETHNICITY) "Federal Race Code" FROM (select distinct FEDETHNICITY FROM Students) t)                         t13,
(SELECT LISTAGG(SUMMERSCHOOLID, ',') within group(order by SUMMERSCHOOLID) "Summer School Key" FROM (select distinct SUMMERSCHOOLID FROM Students) t)                               t14,
(SELECT LISTAGG(FEDRACEDECLINE, ',') within group(order by FEDRACEDECLINE) "Student Decl to Prov Race Code" FROM (select distinct FEDRACEDECLINE FROM Students) t)          t15

Đây là Oracle Database 11g Enterprise Edition Release 11.2.0.2.0 - 64bit Production.
Tôi không thể sử dụng STRAGG vì không có cách nào để CHẨN ĐOÁN và ĐẶT HÀNG.

Hiệu suất chia tỷ lệ tuyến tính, điều này là tốt, vì tôi đang thêm tất cả các cột quan tâm. Ở trên mất 3 giây cho 77K hàng. Chỉ với một lần cuộn lên, 0,172 giây. Tôi làm với có một cách để phân biệt nhiều cột trong một bảng trong một lần chuyển.


6

Nếu bạn muốn các giá trị khác biệt trên MULTIPLE cột, muốn kiểm soát thứ tự sắp xếp, không muốn sử dụng một hàm không có tài liệu có thể biến mất và không muốn quét nhiều bảng, bạn có thể thấy cấu trúc này hữu ích:

with test_data as 
(
      select 'A' as col1, 'T_a1' as col2, '123' as col3 from dual
union select 'A', 'T_a1', '456' from dual
union select 'A', 'T_a1', '789' from dual
union select 'A', 'T_a2', '123' from dual
union select 'A', 'T_a2', '456' from dual
union select 'A', 'T_a2', '111' from dual
union select 'A', 'T_a3', '999' from dual
union select 'B', 'T_a1', '123' from dual
union select 'B', 'T_b1', '740' from dual
union select 'B', 'T_b1', '846' from dual
)
select col1
     , (select listagg(column_value, ',') within group (order by column_value desc) from table(collect_col2)) as col2s
     , (select listagg(column_value, ',') within group (order by column_value desc) from table(collect_col3)) as col3s
from 
(
select col1
     , collect(distinct col2) as collect_col2
     , collect(distinct col3) as collect_col3
from test_data
group by col1
);

1
Bạn có thể tiết kiệm thời gian hơn nếu thay thế "union" bằng "union all".
burkay

4

Còn về việc tạo một chức năng chuyên dụng sẽ làm cho phần "riêng biệt" thì sao:

create or replace function listagg_distinct (t in str_t, sep IN VARCHAR2 DEFAULT ',') 
  return VARCHAR2
as 
  l_rc VARCHAR2(4096) := '';
begin
  SELECT listagg(val, sep) WITHIN GROUP (ORDER BY 1)
    INTO l_rc
    FROM (SELECT DISTINCT column_value val FROM table(t));
  RETURN l_rc;
end;
/

Và sau đó sử dụng nó để tổng hợp:

SELECT col1, listagg_distinct(cast(collect(col_2) as str_t ), ', ')
  FROM your_table
  GROUP BY col_1;

4

Để giải quyết vấn đề độ dài chuỗi, bạn có thể sử dụng XMLAGGtương tự như listaggnhưng nó trả về một tắc nghẽn.

Sau đó, bạn có thể phân tích cú pháp bằng cách sử dụng regexp_replacevà lấy các giá trị duy nhất, sau đó biến nó trở lại thành một chuỗi bằng cách sử dụng dbms_lob.substr(). Nếu bạn có một lượng lớn các giá trị riêng biệt, bạn vẫn sẽ hết dung lượng theo cách này nhưng trong nhiều trường hợp, mã dưới đây sẽ hoạt động.

Bạn cũng có thể thay đổi các dấu phân cách mà bạn sử dụng. Trong trường hợp của tôi, tôi muốn '-' thay vì ',' nhưng bạn có thể thay thế các dấu gạch ngang trong mã của tôi và sử dụng dấu phẩy nếu bạn muốn.

select col1,
    dbms_lob.substr(ltrim(REGEXP_REPLACE(REPLACE(
         REPLACE(
           XMLAGG(
             XMLELEMENT("A",col2)
               ORDER BY col2).getClobVal(),
             '<A>','-'),
             '</A>',''),'([^-]*)(-\1)+($|-)', 
           '\1\3'),'-'), 4000,1) as platform_mix
from table

Đây là một ý tưởng tuyệt vời cần gọi dbms_xmlgen.convert (string, 1) để loại bỏ và & -> & amp chuyển đổi. Xem liên kết
ozmike 29/09/15

3

Tinh chỉnh thêm phần sửa chữa của @ YoYo cho phương pháp tiếp cận dựa trên row_number () của @ a_horse_with_no_name bằng cách sử dụng DECODE so với CASE ( tôi đã thấy ở đây ). Tôi thấy rằng @Martin Vrbovsky cũng có câu trả lời về cách tiếp cận trường hợp này.

select
  col1, 
  listagg(col2, ',') within group (order by col2) AS col2_list,
  listagg(col3, ',') within group (order by col3) AS col3_list,
  SUM(col4) AS col4
from (
  select
    col1, 
    decode(row_number() over (partition by col1, col2 order by null),1,col2) as col2,
    decode(row_number() over (partition by col1, col3 order by null),1,col3) as col3
  from foo
)
group by col1;

2

Oracle 19c sắp tới sẽ hỗ trợ DISTINCTvới LISTAGG.

LISTAGG với tùy chọn DISTINCT :

Tính năng này sẽ ra mắt với 19c:

SQL> select deptno, listagg (distinct sal,', ') within group (order by sal)  
  2  from scott.emp  
  3  group by deptno;  

BIÊN TẬP:

Oracle 19C LISTAGG DISTINCT

Chức năng tổng hợp LISTAGG hiện hỗ trợ loại bỏ trùng lặp bằng cách sử dụng từ khóa DISTINCT mới. Hàm tổng hợp LISTAGG sắp xếp thứ tự các hàng cho mỗi nhóm trong một truy vấn theo biểu thức ORDER BY và sau đó nối các giá trị thành một chuỗi duy nhất. Với từ khóa DISTINCT mới, các giá trị trùng lặp có thể bị xóa khỏi biểu thức được chỉ định trước khi nối thành một chuỗi. Điều này loại bỏ nhu cầu tạo xử lý truy vấn phức tạp để tìm các giá trị riêng biệt trước khi sử dụng hàm LISTAGG tổng hợp. Với tùy chọn DISTINCT, quá trình xử lý để loại bỏ các giá trị trùng lặp có thể được thực hiện trực tiếp trong hàm LISTAGG. Kết quả là SQL đơn giản hơn, nhanh hơn, hiệu quả hơn.


0

Có ai nghĩ đến việc sử dụng mệnh đề PARTITION BY không? Nó hoạt động cho tôi trong truy vấn này để có được danh sách các dịch vụ ứng dụng và quyền truy cập.

SELECT DISTINCT T.APP_SVC_ID, 
       LISTAGG(RTRIM(T.ACCESS_MODE), ',') WITHIN GROUP(ORDER BY T.ACCESS_MODE) OVER(PARTITION BY T.APP_SVC_ID) AS ACCESS_MODE 
  FROM APP_SVC_ACCESS_CNTL T 
 GROUP BY T.ACCESS_MODE, T.APP_SVC_ID

Tôi đã phải cắt bỏ điều khoản where của mình cho NDA, nhưng bạn hiểu rồi đấy.


Tôi không hiểu cách truy vấn này lấy các mục riêng biệt cho LISTAGG. Có vẻ như bạn sẽ chỉ có một T.ACCESS_MODEtrên mỗi hàng vì bạn đang nhóm theo nó?
jpmc26

0

Tôi nghĩ điều này có thể hữu ích - CASE giá trị cột thành NULL nếu nó trùng lặp - thì nó không được nối vào chuỗi LISTAGG:

with test_data as 
(
      select 1 as col1, 2 as col2, 'Smith' as created_by from dual
union select 1, 2, 'John' from dual
union select 1, 3, 'Ajay' from dual
union select 1, 4, 'Ram' from dual
union select 1, 5, 'Jack' from dual
union select 2, 5, 'Smith' from dual
union select 2, 6, 'John' from dual
union select 2, 6, 'Ajay' from dual
union select 2, 6, 'Ram' from dual
union select 2, 7, 'Jack' from dual
)
SELECT col1  ,
      listagg(col2 , ',') within group (order by col2 ASC) AS orig_value,
      listagg(CASE WHEN rwn=1 THEN col2 END , ',') within group (order by col2 ASC) AS distinct_value
from 
    (
    select row_number() over (partition by col1,col2 order by 1) as rwn, 
           a.*
    from test_data a
    ) a
GROUP BY col1   

Kết quả trong:

COL1  ORIG         DISTINCT
1   2,2,3,4,5   2,3,4,5
2   5,6,6,6,7   5,6,7

0

listagg () bỏ qua các giá trị NULL, vì vậy trong bước đầu tiên, bạn có thể sử dụng hàm lag () để phân tích xem bản ghi trước đó có cùng giá trị hay không, nếu có thì NULL, khác là 'giá trị mới'.

WITH tab AS 
(           
          SELECT 1 as col1, 2 as col2, 'Smith' as created_by FROM dual
UNION ALL SELECT 1 as col1, 2 as col2, 'John'  as created_by FROM dual
UNION ALL SELECT 1 as col1, 3 as col2, 'Ajay'  as created_by FROM dual
UNION ALL SELECT 1 as col1, 4 as col2, 'Ram'   as created_by FROM dual
UNION ALL SELECT 1 as col1, 5 as col2, 'Jack'  as created_by FROM dual
)
SELECT col1
     , CASE 
       WHEN lag(col2) OVER (ORDER BY col2) = col2 THEN 
         NULL 
       ELSE 
         col2 
       END as col2_with_nulls
     , created_by
  FROM tab;

Các kết quả

      COL1 COL2_WITH_NULLS CREAT
---------- --------------- -----
         1               2 Smith
         1                 John
         1               3 Ajay
         1               4 Ram
         1               5 Jack

Lưu ý rằng số 2 thứ hai được thay thế bằng NULL. Bây giờ bạn có thể bao bọc một SELECT với listagg () xung quanh nó.

WITH tab AS 
(           
          SELECT 1 as col1, 2 as col2, 'Smith' as created_by FROM dual
UNION ALL SELECT 1 as col1, 2 as col2, 'John'  as created_by FROM dual
UNION ALL SELECT 1 as col1, 3 as col2, 'Ajay'  as created_by FROM dual
UNION ALL SELECT 1 as col1, 4 as col2, 'Ram'   as created_by FROM dual
UNION ALL SELECT 1 as col1, 5 as col2, 'Jack'  as created_by FROM dual
)
SELECT listagg(col2_with_nulls, ',') WITHIN GROUP (ORDER BY col2_with_nulls) col2_list
  FROM ( SELECT col1
              , CASE WHEN lag(col2) OVER (ORDER BY col2) = col2 THEN NULL ELSE col2 END as col2_with_nulls
              , created_by
           FROM tab );

Kết quả

COL2_LIST
---------
2,3,4,5

Bạn cũng có thể làm điều này trên nhiều cột.

WITH tab AS 
(           
          SELECT 1 as col1, 2 as col2, 'Smith' as created_by FROM dual
UNION ALL SELECT 1 as col1, 2 as col2, 'John'  as created_by FROM dual
UNION ALL SELECT 1 as col1, 3 as col2, 'Ajay'  as created_by FROM dual
UNION ALL SELECT 1 as col1, 4 as col2, 'Ram'   as created_by FROM dual
UNION ALL SELECT 1 as col1, 5 as col2, 'Jack'  as created_by FROM dual
)
SELECT listagg(col1_with_nulls, ',') WITHIN GROUP (ORDER BY col1_with_nulls) col1_list
     , listagg(col2_with_nulls, ',') WITHIN GROUP (ORDER BY col2_with_nulls) col2_list
     , listagg(created_by, ',')      WITHIN GROUP (ORDER BY created_by) created_by_list
  FROM ( SELECT CASE WHEN lag(col1) OVER (ORDER BY col1) = col1 THEN NULL ELSE col1 END as col1_with_nulls
              , CASE WHEN lag(col2) OVER (ORDER BY col2) = col2 THEN NULL ELSE col2 END as col2_with_nulls
              , created_by
           FROM tab );

Kết quả

COL1_LIST COL2_LIST CREATED_BY_LIST
--------- --------- -------------------------
1         2,3,4,5   Ajay,Jack,John,Ram,Smith

0

Bạn có thể làm điều đó thông qua thay thế RegEx. Đây là một ví dụ:

-- Citations Per Year - Cited Publications main query. Includes list of unique associated core project numbers, ordered by core project number.
SELECT ptc.pmid AS pmid, ptc.pmc_id, ptc.pub_title AS pubtitle, ptc.author_list AS authorlist,
  ptc.pub_date AS pubdate,
  REGEXP_REPLACE( LISTAGG ( ppcc.admin_phs_org_code || 
    TO_CHAR(ppcc.serial_num,'FM000000'), ',') WITHIN GROUP (ORDER BY ppcc.admin_phs_org_code || 
    TO_CHAR(ppcc.serial_num,'FM000000')),
    '(^|,)(.+)(,\2)+', '\1\2')
  AS projectNum
FROM publication_total_citations ptc
  JOIN proj_paper_citation_counts ppcc
    ON ptc.pmid = ppcc.pmid
   AND ppcc.citation_year = 2013
  JOIN user_appls ua
    ON ppcc.admin_phs_org_code = ua.admin_phs_org_code
   AND ppcc.serial_num = ua.serial_num
   AND ua.login_id = 'EVANSF'
GROUP BY ptc.pmid, ptc.pmc_id, ptc.pub_title, ptc.author_list, ptc.pub_date
ORDER BY pmid;

Cũng được đăng ở đây: Oracle - giá trị Listagg duy nhất


0

Sử dụng hàm listagg_clob được tạo như thế này:

create or replace package list_const_p
is
list_sep varchar2(10) := ',';
end list_const_p;
/
sho err

create type listagg_clob_t as object(
v_liststring varchar2(32767),
v_clob clob,
v_templob number,

static function ODCIAggregateInitialize(
sctx IN OUT listagg_clob_t
) return number,
member function ODCIAggregateIterate(
self IN OUT listagg_clob_t, value IN varchar2
) return number,
member function ODCIAggregateTerminate(
self IN OUT listagg_clob_t, returnValue OUT clob, flags IN number
) return number,
member function ODCIAggregateMerge(
self IN OUT listagg_clob_t, ctx2 IN OUT listagg_clob_t
) return number
);
/
sho err

create or replace type body listagg_clob_t is

static function ODCIAggregateInitialize(sctx IN OUT listagg_clob_t)
return number is
begin
sctx := listagg_clob_t('', '', 0);
return ODCIConst.Success;
end;

member function ODCIAggregateIterate(
self IN OUT listagg_clob_t,
value IN varchar2
) return number is
begin
if nvl(lengthb(v_liststring),0) + nvl(lengthb(value),0) <= 4000 then
self.v_liststring:=self.v_liststring || value || list_const_p.list_sep;
else
if self.v_templob = 0 then
dbms_lob.createtemporary(self.v_clob, true, dbms_lob.call);
self.v_templob := 1;
end if;
dbms_lob.writeappend(self.v_clob, length(self.v_liststring), v_liststring);
self.v_liststring := value || list_const_p.list_sep;
end if;
return ODCIConst.Success;
end;

member function ODCIAggregateTerminate(
self IN OUT listagg_clob_t,
returnValue OUT clob,
flags IN number
) return number is
begin
if self.v_templob != 0 then
dbms_lob.writeappend(self.v_clob, length(self.v_liststring), self.v_liststring);
dbms_lob.trim(self.v_clob, dbms_lob.getlength(self.v_clob) - 1);
else
self.v_clob := substr(self.v_liststring, 1, length(self.v_liststring) - 1);
end if;
returnValue := self.v_clob;
return ODCIConst.Success;
end;

member function ODCIAggregateMerge(self IN OUT listagg_clob_t, ctx2 IN OUT listagg_clob_t) return number is
begin
if ctx2.v_templob != 0 then
if self.v_templob != 0 then
dbms_lob.append(self.v_clob, ctx2.v_clob);
dbms_lob.freetemporary(ctx2.v_clob);
ctx2.v_templob := 0;
else
self.v_clob := ctx2.v_clob;
self.v_templob := 1;
ctx2.v_clob := '';
ctx2.v_templob := 0;
end if;
end if;
if nvl(lengthb(self.v_liststring),0) + nvl(lengthb(ctx2.v_liststring),0) <= 4000 then
self.v_liststring := self.v_liststring || ctx2.v_liststring;
ctx2.v_liststring := '';
else
if self.v_templob = 0 then
dbms_lob.createtemporary(self.v_clob, true, dbms_lob.call);
self.v_templob := 1;
end if;
dbms_lob.writeappend(self.v_clob, length(self.v_liststring), self.v_liststring);
dbms_lob.writeappend(self.v_clob, length(ctx2.v_liststring), ctx2.v_liststring);
self.v_liststring := '';
ctx2.v_liststring := '';
end if;
return ODCIConst.Success;
end;
end;
/
sho err

CREATE or replace FUNCTION listagg_clob (input varchar2) RETURN clob
PARALLEL_ENABLE AGGREGATE USING listagg_clob_t;
/
sho err 


0

Tôi đã viết một hàm để xử lý điều này bằng cách sử dụng biểu thức chính quy. Các tham số trong là: 1) chính nó gọi listagg 2) Dấu phân cách lặp lại

create or replace function distinct_listagg
  (listagg_in varchar2,
   delimiter_in varchar2)

   return varchar2
   as
   hold_result varchar2(4000);
   begin

   select rtrim( regexp_replace( (listagg_in)
      , '([^'||delimiter_in||']*)('||
      delimiter_in||'\1)+($|'||delimiter_in||')', '\1\3'), ',')
      into hold_result
      from dual;

return hold_result;

end;

Giờ đây, bạn không phải lặp lại biểu thức chính quy mỗi khi làm điều này, chỉ cần nói:

select distinct_listagg(
                       listagg(myfield,', ') within group (order by 1),
                       ', '
                       )
     from mytable;

0

Nếu bạn không cần một thứ tự cụ thể của các giá trị được nối và dấu phân tách có thể là dấu phẩy, bạn có thể thực hiện:

select col1, stragg(distinct col2)
  from table
 group by col1

0

Tôi đã nhập một phiên bản DISTINCT của phiên bản này và phiên bản này đang hoạt động.

RTRIM(REGEXP_REPLACE(
                       (value, ', ') WITHIN GROUP( ORDER BY value)), 
                            '([^ ]+)(, \1)+','\1'),', ') 

0

Một khía cạnh khó chịu LISTAGGlà nếu tổng độ dài của chuỗi nối vượt quá 4000 ký tự (giới hạn VARCHAR2trong SQL), lỗi dưới đây sẽ xảy ra, rất khó quản lý trong các phiên bản Oracle tối đa 12.1

ORA-01489: kết quả của việc nối chuỗ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ẽ giống như sau:

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

Ở trên sẽ giới hạn đầu ra ở 4000 ký tự nhưng sẽ không ORA-01489gây ra lỗ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 nào.
  • ON OVERFLOW TRUNCATE WITH COUNT: Điều này sẽ hiển thị tổng số ký tự ở cuối sau các ký tự kết thúc. Vd: - ' ...(5512)'
  • ON OVERFLOW ERROR: Nếu bạn mong đợi LISTAGGkhông thành công với ORA-01489lỗi (Dù sao vẫn là mặc định).

0

Tôi đã triển khai chức năng được lưu trữ này:

CREATE TYPE LISTAGG_DISTINCT_PARAMS AS OBJECT (ELEMENTO VARCHAR2(2000), SEPARATORE VARCHAR2(10));

CREATE TYPE T_LISTA_ELEMENTI AS TABLE OF VARCHAR2(2000);

CREATE TYPE T_LISTAGG_DISTINCT AS OBJECT (

    LISTA_ELEMENTI T_LISTA_ELEMENTI,
        SEPARATORE VARCHAR2(10),

    STATIC FUNCTION ODCIAGGREGATEINITIALIZE(SCTX  IN OUT            T_LISTAGG_DISTINCT) 
                    RETURN NUMBER,

    MEMBER FUNCTION ODCIAGGREGATEITERATE   (SELF  IN OUT            T_LISTAGG_DISTINCT, 
                                            VALUE IN                    LISTAGG_DISTINCT_PARAMS ) 
                    RETURN NUMBER,

    MEMBER FUNCTION ODCIAGGREGATETERMINATE (SELF         IN     T_LISTAGG_DISTINCT,
                                            RETURN_VALUE OUT    VARCHAR2, 
                                            FLAGS        IN     NUMBER      )
                    RETURN NUMBER,

    MEMBER FUNCTION ODCIAGGREGATEMERGE       (SELF               IN OUT T_LISTAGG_DISTINCT,
                                                                                        CTX2                 IN         T_LISTAGG_DISTINCT    )
                    RETURN NUMBER
);

CREATE OR REPLACE TYPE BODY T_LISTAGG_DISTINCT IS 

    STATIC FUNCTION ODCIAGGREGATEINITIALIZE(SCTX IN OUT T_LISTAGG_DISTINCT) RETURN NUMBER IS 
    BEGIN
                SCTX := T_LISTAGG_DISTINCT(T_LISTA_ELEMENTI() , ',');
        RETURN ODCICONST.SUCCESS;
    END;

    MEMBER FUNCTION ODCIAGGREGATEITERATE(SELF IN OUT T_LISTAGG_DISTINCT, VALUE IN LISTAGG_DISTINCT_PARAMS) RETURN NUMBER IS
    BEGIN

                IF VALUE.ELEMENTO IS NOT NULL THEN
                        SELF.LISTA_ELEMENTI.EXTEND;
                        SELF.LISTA_ELEMENTI(SELF.LISTA_ELEMENTI.LAST) := TO_CHAR(VALUE.ELEMENTO);
                        SELF.LISTA_ELEMENTI:= SELF.LISTA_ELEMENTI MULTISET UNION DISTINCT SELF.LISTA_ELEMENTI;
                        SELF.SEPARATORE := VALUE.SEPARATORE;
                END IF;
        RETURN ODCICONST.SUCCESS;
    END;

    MEMBER FUNCTION ODCIAGGREGATETERMINATE(SELF IN T_LISTAGG_DISTINCT, RETURN_VALUE OUT VARCHAR2, FLAGS IN NUMBER) RETURN NUMBER IS
      STRINGA_OUTPUT            CLOB:='';
            LISTA_OUTPUT                T_LISTA_ELEMENTI;
            TERMINATORE                 VARCHAR2(3):='...';
            LUNGHEZZA_MAX           NUMBER:=4000;
    BEGIN

                IF SELF.LISTA_ELEMENTI.EXISTS(1) THEN -- se esiste almeno un elemento nella lista

                        -- inizializza una nuova lista di appoggio
                        LISTA_OUTPUT := T_LISTA_ELEMENTI();

                        -- riversamento dei soli elementi in DISTINCT
                        LISTA_OUTPUT := SELF.LISTA_ELEMENTI MULTISET UNION DISTINCT SELF.LISTA_ELEMENTI;

                        -- ordinamento degli elementi
                        SELECT CAST(MULTISET(SELECT * FROM TABLE(LISTA_OUTPUT) ORDER BY 1 ) AS T_LISTA_ELEMENTI ) INTO LISTA_OUTPUT FROM DUAL;

                        -- concatenazione in una stringa                        
                        FOR I IN LISTA_OUTPUT.FIRST .. LISTA_OUTPUT.LAST - 1
                        LOOP
                            STRINGA_OUTPUT := STRINGA_OUTPUT || LISTA_OUTPUT(I) || SELF.SEPARATORE;
                        END LOOP;
                        STRINGA_OUTPUT := STRINGA_OUTPUT || LISTA_OUTPUT(LISTA_OUTPUT.LAST);

                        -- se la stringa supera la dimensione massima impostata, tronca e termina con un terminatore
                        IF LENGTH(STRINGA_OUTPUT) > LUNGHEZZA_MAX THEN
                                    RETURN_VALUE := SUBSTR(STRINGA_OUTPUT, 0, LUNGHEZZA_MAX - LENGTH(TERMINATORE)) || TERMINATORE;
                        ELSE
                                    RETURN_VALUE:=STRINGA_OUTPUT;
                        END IF;

                ELSE -- se non esiste nessun elemento, restituisci NULL

                        RETURN_VALUE := NULL;

                END IF;

        RETURN ODCICONST.SUCCESS;
    END;

    MEMBER FUNCTION ODCIAGGREGATEMERGE(SELF IN OUT T_LISTAGG_DISTINCT, CTX2 IN T_LISTAGG_DISTINCT) RETURN NUMBER IS
    BEGIN
        RETURN ODCICONST.SUCCESS;
    END;

END; -- fine corpo

CREATE
FUNCTION LISTAGG_DISTINCT (INPUT LISTAGG_DISTINCT_PARAMS) RETURN VARCHAR2
    PARALLEL_ENABLE AGGREGATE USING T_LISTAGG_DISTINCT;

// Example
SELECT LISTAGG_DISTINCT(LISTAGG_DISTINCT_PARAMS(OWNER, ', ')) AS LISTA_OWNER
FROM SYS.ALL_OBJECTS;

Tôi xin lỗi, nhưng trong một số trường hợp (đối với một tập hợp rất lớn), Oracle có thể trả lại lỗi này:

Object or Collection value was too large. The size of the value
might have exceeded 30k in a SORT context, or the size might be
too big for available memory.

nhưng tôi nghĩ đây là một điểm khởi đầu tốt;)


0

select col1, listaggr(col2,',') within group(Order by col2) from table group by col1 có nghĩa là tổng hợp các chuỗi (col2) thành danh sách theo thứ tự n sau đó xử lý các bản sao theo nhóm theo col1 nghĩa là hợp nhất các bản sao col1 trong 1 nhóm. có lẽ điều này trông sạch sẽ và đơn giản như nó nên có và nếu trong trường hợp bạn muốn col3, bạn chỉ cần thêm một listagg () nữa đó làselect col1, listaggr(col2,',') within group(Order by col2),listaggr(col3,',') within group(order by col3) from table group by col1


0

Sử dụng SELECT DISTINCT ...như một phần của Truy vấn con trước khi gọi LISTAGG có lẽ là cách tốt nhất cho các truy vấn đơn giản, như @a_horse_with_no_name đã lưu ý

Tuy nhiên, trong các truy vấn phức tạp hơn, có thể không thực hiện được hoặc dễ dàng. Tôi đã đưa ra điều này trong một tình huống sử dụng phương pháp tiếp cận top-n bằng cách sử dụng một hàm phân tích.

Vì vậy, tôi đã tìm thấy COLLECThàm tổng hợp. Nó được ghi lại là có sẵn UNIQUEhoặc DISTINCTsửa đổi. Chỉ trong 10g , nó lặng lẽ bị lỗi (nó bỏ qua công cụ sửa đổi mà không có lỗi). Tuy nhiên, để khắc phục điều này, từ một câu trả lời khác , tôi đã đi đến giải pháp này:

SELECT
  ...
  (
    SELECT LISTAGG(v.column_value,',') WITHIN GROUP (ORDER BY v.column_value)
    FROM TABLE(columns_tab) v
  ) AS columns,
  ...
FROM (
  SELECT
    ...
    SET(CAST(COLLECT(UNIQUE some_column ORDER BY some_column) AS tab_typ)) AS columns_tab,
    ...
)

Về cơ bản, bằng cách sử dụng SET, tôi loại bỏ các bản sao trong bộ sưu tập của mình.

Bạn vẫn cần phải xác định tab_typkiểu tập hợp cơ bản và trong trường hợp của a VARCHAR, ví dụ:

CREATE OR REPLACE type tab_typ as table of varchar2(100)
/

Cũng như một sự chỉnh sửa cho câu trả lời từ @a_horse_with_no_name trong trường hợp nhiều cột, nơi bạn có thể muốn tổng hợp vẫn trên một (hoặc nhiều) cột thứ ba:

select
  col1, 
  listagg(CASE rn2 WHEN 1 THEN col2 END, ',') within group (order by col2) AS col2_list,
  listagg(CASE rn3 WHEN 1 THEN col3 END, ',') within group (order by col3) AS col3_list,
  SUM(col4) AS col4
from (
  select
    col1, 
    col2,
    row_number() over (partition by col1, col2 order by null) as rn2,
    row_number() over (partition by col1, col3 order by null) as rn3
  from foo
)
group by col1;

Nếu bạn để rn = 1điều kiện ở đâu cho truy vấn, bạn sẽ tổng hợp các cột khác không chính xác.


0

Rất đơn giản - sử dụng trong truy vấn của bạn một truy vấn phụ với một lựa chọn riêng biệt:

SELECT question_id,
       LISTAGG(element_id, ',') WITHIN GROUP (ORDER BY element_id)
FROM
       (SELECT distinct question_id, element_id
       FROM YOUR_TABLE)
GROUP BY question_id;

-1

Cách đơn giản nhất để xử lý nhiều thẻ danh sách là sử dụng 1 VỚI (hệ số truy vấn con) trên mỗi cột chứa thẻ danh sách của cột đó từ một danh sách riêng biệt được chọn:

    WITH tab AS 
    (           
        SELECT 1 as col1, 2 as col2, 3 as col3, 'Smith' as created_by FROM dual
        UNION ALL SELECT 1 as col1, 2 as col2, 3 as col3,'John'  as created_by FROM dual
        UNION ALL SELECT 1 as col1, 3 as col2, 4 as col3,'Ajay'  as created_by FROM dual
        UNION ALL SELECT 1 as col1, 4 as col2, 4 as col3,'Ram'   as created_by FROM dual
        UNION ALL SELECT 1 as col1, 5 as col2, 6 as col3,'Jack'  as created_by FROM dual
    )
    , getCol2 AS
    (
        SELECT  DISTINCT col1, listagg(col2,',') within group (order by col2)  over (partition by col1) AS col2List
        FROM ( SELECT DISTINCT col1,col2 FROM tab)
    )
    , getCol3 AS
    (
        SELECT  DISTINCT col1, listagg(col3,',') within group (order by col3)  over (partition by col1) AS col3List
        FROM ( SELECT DISTINCT col1,col3 FROM tab)
    )
    select col1,col2List,col3List
    FROM getCol2
    JOIN getCol3
    using (col1)

Cái nào mang lại:

col1  col2List  col3List
1     2,3,4,5   3,4,6
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.