LISTAGG di Oracle untuk mengembalikan nilai yang berbeda


94

Saya mencoba menggunakan LISTAGGfungsi di Oracle. Saya hanya ingin mendapatkan nilai yang berbeda untuk kolom itu. Apakah ada cara di mana saya hanya bisa mendapatkan nilai yang berbeda tanpa membuat fungsi atau prosedur?

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

Saya perlu memilih col1 dan LISTAGGdari col2 (kolom 3 tidak dipertimbangkan). Ketika saya melakukan itu, saya mendapatkan sesuatu seperti ini sebagai hasil dari LISTAGG: [2,2,3,4,5]

Saya perlu menghapus duplikat '2' di sini; Saya hanya membutuhkan nilai berbeda dari col2 terhadap col1.


5
Pertanyaan ini telah ditanyakan pada Administrator Database : Hapus duplikat di ListAgg (Oracle)
Andriy M

Dapatkah Anda menunjukkan ouptut (baris) yang diharapkan dari sampel? Apa yang ingin Anda lihat jika ada lebih dari satu nilai untuk col1?
a_horse_with_no_name

Output yang diharapkan dari LISTAGG adalah [2,3,4,5]. '2' kedua harus dihilangkan. Dan tabel saya memiliki lebih dari 1000 baris.
Priyanth

Apa yang ingin Anda lihat jika ada lebih dari satu nilai untuk col1?
a_horse_with_no_name

Kodenya seperti ini: - PILIH col1, LISTAGG (col2, ',') di dalam grup (diurutkan menurut col2) DARI tabel T WHERE .... Jadi, itu harus menunjukkan semua nilai berbeda dari col2 yang sesuai dengan col1, dipisahkan oleh koma.
Priyanth

Jawaban:


77

19c dan lebih baru:

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

18c dan sebelumnya:

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

Jika Anda membutuhkan lebih banyak kolom, sesuatu seperti ini mungkin yang Anda cari:

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
Mirip dengan apa yang ada dalam pikiran saya juga. Jika listaggmerupakan satu-satunya fungsi agregat dalam kueri, ini harus dilakukan. Menggabungkannya dengan fungsi agregat lainnya, bagaimanapun, lebih rumit.
Andriy M

Iya. Permintaan saya mirip dengan ini.
Priyanth

1
@a_horse_with_no_name: Pernyataan pilih di atas memberikan nilai duplikat untuk saya. Saya ingin menghapus duplikat. col1 col2 Dibuat oleh 1 2 Smith 1 2 John 1 3 Ajay 1 4 Ram 1 5 Jack Saya perlu memilih col1 dan LISTAGG dari col2 (kolom 3 tidak dipertimbangkan). Sementara saya melakukan itu saya akan mendapatkan sesuatu seperti ini sebagai hasil dari LISTAGG: -> [2,2,3,4,5] Saya perlu menghapus duplikat'2 'di sini. Saya hanya perlu nilai yang berbeda dari col2 terhadap col1 .
Priyanth

@a_horse_with_no_name: Saya mencoba kode- dan mendapatkan pesan error seperti di bawah ini ORA-01489: hasil penggabungan string terlalu panjang 01489. 00000 - "hasil penggabungan string terlalu panjang" * Penyebab: hasil penggabungan string lebih dari maksimum ukuran.
Priyanth

@ Priyanth: maka Anda kurang beruntung. Panjang total melebihi 4000 byte dan Oracle tidak dapat mengatasinya. Anda perlu melakukan agregasi dalam kode aplikasi Anda.
a_horse_with_no_name

47

Berikut cara mengatasi masalah Anda.

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

from dual

kembali

2,2.1,3,4

Dari oracle 19C dibangun lihat di sini

Dari 18C dan sebelumnya coba dalam lihat kelompok di sini

Jika tidak, gunakan ekspresi reguler

JAWABAN di bawah ini:

select col1, 

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

Catatan: Cara di atas akan berfungsi dalam banyak kasus - daftar harus diurutkan, Anda mungkin harus memangkas semua spasi di belakang dan di depan tergantung pada data Anda.

Jika Anda memiliki banyak item dalam grup> 20 atau ukuran string besar Anda mungkin mengalami batas ukuran string oracle 'hasil rangkaian string terlalu panjang'.

Dari oracle 12cR2 Anda dapat menekan kesalahan ini, lihat di sini . Atau berikan jumlah maksimal pada anggota di setiap grup. Ini hanya akan berfungsi jika tidak masalah untuk mencantumkan hanya anggota pertama. Jika Anda memiliki string variabel yang sangat panjang, ini mungkin tidak berfungsi. Anda harus bereksperimen.

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;

Solusi lain (tidak sesederhana itu) untuk menghindari batas ukuran string oracle - ukuran string dibatasi hingga 4000. Terima kasih untuk posting ini di sini oleh user3465996

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 - beberapa kasus uji - 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 -item yang terkandung dalam item mis. 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 terima kasih Igor! bekerja di semua kasus.

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
Hasil yang adil, tetapi tidak sesederhana itu. Dengan ukuran data yang serius Anda akan mengalami ORA-01489: result of string concatenation is too long.
Pero

1
Saya tidak akan menyebutnya sebagai solusi yang sederhana tetapi sangat menarik. Saya tidak tahu bahwa nomor yang cocok dapat digunakan dalam string pencarian tidak hanya string pengganti. Briliant.
Peter Krassoi

1
Sebagai peringatan, metode ini mengharuskan nilai diurutkan, sehingga nilai duplikatnya berurutan. Jika tidak, itu gagal. Tapi sederhana itu bagus! Dan saya menggunakan metode ini untuk kasus khusus saya. Terima kasih!
StewS2

2
super sederhana tidak bekerja untuk lebih dari 3 pengulangan! , misalnya a,b,b,b,b,cakan menjadi a,b,b,c:-( (Oracle 11.2)
Andreas Dietrich

4
@AndreasDietrich - Solusi berikut tampaknya selalu benar:regexp_replace(your_string, '([^,]+)(,\1)*(,|$)', '\1\3')
Egor Skriptunoff

10

Anda bisa menggunakan wm_concatfungsi tidak berdokumen .

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

fungsi ini mengembalikan kolom clob, jika Anda mau, Anda dapat menggunakan dbms_lob.substruntuk mengonversi clob ke varchar2.


15
Tidak, jangan gunakan itu.
Koshinae

1
Inilah yang saya butuhkan, dan bekerja dengan sempurna dalam kueri gabungan saya yang ada alih-alih membungkus kueri itu di kueri terluar. Apa salahnya menggunakan wm_concat(distinct x)?
Ehryk

1
karena tidak didokumentasikan dan tidak ada di 12c. tapi bagaimanapun pada versi lama saya pikir ini adalah cara terbaik.
Kemalettin Erbakırcı

1
Terima kasih @ kemalettinerbakırcı! @thg Anda harus mempertimbangkan bahwa jika ada sesuatu yang tidak terdokumentasi, Anda tidak tahu apa efek sampingnya, dan hal-hal lain yang diberitahukan oleh Dokumentasi kepada Anda tentang fungsi yang didokumentasikan; Anda hanya menggunakannya sebagai kotak hitam dan Anda hanya tahu tuas mana yang melakukan apa yang berdasarkan cerita rakyat.
Koshinae

4
Jangan pernah gunakan wm_concat. Lihat Mengapa tidak menggunakan fungsi WM_CONCAT di Oracle? .
Lalit Kumar B

7

Saya mengatasi masalah ini dengan mengelompokkan nilai-nilai terlebih dahulu, kemudian melakukan agregasi lain dengan listagg. Sesuatu seperti ini:

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)

hanya satu akses tabel penuh, relatif mudah diperluas ke kueri yang lebih kompleks


6

Jika tujuannya adalah untuk menerapkan transformasi ini ke beberapa kolom, saya telah memperluas solusi 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

Ini adalah Oracle Database 11g Enterprise Edition Release 11.2.0.2.0 - Produksi 64bit.
Saya tidak dapat menggunakan STRAGG karena tidak ada cara untuk MEMBEDAKAN dan MEMESAN.

Skala kinerja secara linier, yang bagus, karena saya menambahkan semua kolom yang menarik. Di atas membutuhkan waktu 3 detik untuk 77K baris. Hanya untuk satu rollup, 0,172 detik. Saya lakukan dengan ada cara untuk membedakan beberapa kolom dalam satu tabel dalam satu kali jalan.


6

Jika Anda menginginkan nilai yang berbeda di beberapa kolom, ingin mengontrol urutan, tidak ingin menggunakan fungsi tidak terdokumentasi yang mungkin hilang, dan tidak ingin lebih dari satu pemindaian tabel penuh, Anda mungkin menganggap konstruksi ini berguna:

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
Anda dapat menghemat lebih banyak waktu jika mengganti "union" dengan "union all".
burkay

4

Bagaimana dengan membuat fungsi khusus yang akan membuat bagian "berbeda":

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

Dan kemudian gunakan untuk melakukan agregasi:

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

4

Untuk mengatasi masalah panjang string Anda dapat menggunakan XMLAGGyang mirip denganlistagg tetapi mengembalikan gumpalan.

Anda kemudian dapat mengurai menggunakan regexp_replacedan mendapatkan nilai unik dan kemudian mengubahnya kembali menjadi string menggunakandbms_lob.substr() . Jika Anda memiliki sejumlah besar nilai berbeda, Anda masih akan kehabisan ruang dengan cara ini tetapi untuk banyak kasus, kode di bawah ini seharusnya berfungsi.

Anda juga dapat mengubah pembatas yang Anda gunakan. Dalam kasus saya, saya menginginkan '-' daripada ',' tetapi Anda harus dapat mengganti tanda hubung dalam kode saya dan menggunakan koma jika Anda menginginkannya.

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

Ini adalah ide yang bagus perlu memanggil dbms_xmlgen.convert (string, 1) untuk menghapus dan & -> & amp konversi. Lihat tautan
ozmike

3

Lebih lanjut menyempurnakan koreksi @ YoYo untuk pendekatan berbasis row_number () @ a_horse_with_no_name menggunakan DECODE vs CASE ( saya lihat di sini ). Saya melihat bahwa @Martin Vrbovsky juga memiliki jawaban pendekatan kasus ini.

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 yang akan datang akan mendukung DISTINCTdengan LISTAGG.

LISTAGG dengan opsi DISTINCT :

Fitur ini hadir dengan 19c:

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

EDIT:

Oracle 19C LISTAGG DISTINCT

Fungsi agregat LISTAGG sekarang mendukung penghapusan duplikat dengan menggunakan kata kunci DISTINCT yang baru. Fungsi agregat LISTAGG mengurutkan baris untuk setiap grup dalam kueri sesuai dengan ekspresi ORDER BY dan kemudian menggabungkan nilai-nilai menjadi satu string. Dengan kata kunci DISTINCT baru, nilai duplikat dapat dihapus dari ekspresi yang ditentukan sebelum penggabungan menjadi satu string. Ini menghilangkan kebutuhan untuk membuat pemrosesan kueri yang kompleks untuk menemukan nilai yang berbeda sebelum menggunakan fungsi LISTAGG gabungan. Dengan opsi DISTINCT, pemrosesan untuk menghapus nilai duplikat dapat dilakukan secara langsung di dalam fungsi LISTAGG. Hasilnya adalah SQL yang lebih sederhana, lebih cepat, dan lebih efisien.


0

Adakah yang pernah berpikir untuk menggunakan klausa PARTITION BY? Saya berhasil dalam kueri ini untuk mendapatkan daftar layanan aplikasi dan aksesnya.

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

Saya harus memotong klausa where saya untuk NDA, tetapi Anda mengerti.


Saya tidak mengerti bagaimana kueri ini mengambil item yang berbeda untuk LISTAGG. Tampaknya Anda hanya akan memiliki satu T.ACCESS_MODEper baris karena Anda mengelompokkannya?
jpmc26

0

Saya pikir ini bisa membantu - KASUS nilai kolom ke NULL jika itu duplikat - maka itu tidak ditambahkan ke string 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   

Hasil dalam:

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

0

listagg () mengabaikan nilai NULL, jadi pada langkah pertama Anda dapat menggunakan fungsi lag () untuk menganalisis apakah record sebelumnya memiliki nilai yang sama, jika ya maka NULL, atau 'nilai baru'.

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;

Hasil

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

Perhatikan bahwa 2 kedua diganti dengan NULL. Sekarang Anda dapat membungkus SELECT dengan listagg () di sekitarnya.

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

Hasil

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

Anda juga dapat melakukan ini pada beberapa kolom.

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

Hasil

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

0

Anda dapat melakukannya melalui penggantian RegEx. Berikut ini contohnya:

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

Juga diposting di sini: Oracle - nilai Listagg unik


0

Gunakan fungsi listagg_clob yang dibuat seperti ini:

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

Saya menulis fungsi untuk menangani ini menggunakan ekspresi reguler. Parameter in adalah: 1) listagg memanggil dirinya sendiri 2) Pengulangan delimiter

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;

Sekarang Anda tidak perlu mengulangi ekspresi reguler setiap kali melakukan ini, cukup ucapkan:

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

0

Jika Anda tidak memerlukan urutan nilai gabungan tertentu, dan pemisah dapat berupa koma, Anda dapat melakukan:

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

0

Saya membutuhkan versi yang BERBEDA dari ini dan yang ini berhasil.

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

0

Salah satu aspek yang mengganggu LISTAGGadalah jika total panjang string gabungan melebihi 4000 karakter (batas untuk VARCHAR2SQL), kesalahan di bawah ini muncul, yang sulit untuk dikelola dalam versi Oracle hingga 12.1

ORA-01489: hasil penggabungan string terlalu panjang

Fitur baru yang ditambahkan di 12cR2 adalah ON OVERFLOWklausul LISTAGG. Kueri yang menyertakan klausul ini akan terlihat seperti:

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

Di atas akan membatasi keluaran menjadi 4000 karakter tetapi tidak akan membuang file ORA-01489 kesalahan.

Ini adalah beberapa opsi tambahan dari ON OVERFLOWklausa:

  • ON OVERFLOW TRUNCATE 'Contd..' : Ini akan ditampilkan 'Contd..'di akhir string (Default adalah... )
  • ON OVERFLOW TRUNCATE '' : Ini akan menampilkan 4000 karakter tanpa string yang mengakhiri.
  • ON OVERFLOW TRUNCATE WITH COUNT: Ini akan menampilkan jumlah karakter di akhir setelah karakter yang diakhiri. Misalnya: - '...(5512) '
  • ON OVERFLOW ERROR: Jika Anda mengharapkan LISTAGGgagal dengan ORA-01489kesalahan (Yang tetap default).

0

Saya menerapkan fungsi tersimpan ini:

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;

Maaf, tetapi dalam beberapa kasus (untuk set yang sangat besar), Oracle dapat mengembalikan kesalahan ini:

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.

tapi saya pikir ini adalah titik awal yang baik;)


0

select col1, listaggr(col2,',') within group(Order by col2) from table group by col1 Artinya menggabungkan string (col2) ke dalam daftar dengan menjaga urutan n kemudian kemudian menangani duplikat sebagai grup dengan col1 yang berarti menggabungkan duplikat col1 dalam 1 grup. mungkin ini terlihat bersih dan sederhana sebagaimana mestinya dan jika Anda ingin col3 juga hanya Anda perlu menambahkan satu listagg () lagiselect col1, listaggr(col2,',') within group(Order by col2),listaggr(col3,',') within group(order by col3) from table group by col1


0

Menggunakan SELECT DISTINCT ...sebagai bagian dari Subquery sebelum memanggil LISTAGG mungkin adalah cara terbaik untuk kueri sederhana, seperti dicatat oleh @a_horse_with_no_name

Namun, dalam kueri yang lebih kompleks, hal ini mungkin tidak dapat dilakukan atau mudah dilakukan. Saya mendapatkan ini dalam skenario yang menggunakan pendekatan top-n menggunakan fungsi analitik.

Jadi saya menemukan COLLECTfungsi agregat. Ini didokumentasikan untuk memiliki UNIQUEatau DISTINCTpengubah tersedia. Hanya dalam 10g , itu diam-diam gagal (mengabaikan pengubah tanpa kesalahan). Namun, untuk mengatasinya, dari jawaban lain , saya sampai pada solusi ini:

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,
    ...
)

Pada dasarnya, dengan menggunakan SET, saya menghapus duplikat dalam koleksi saya.

Anda masih perlu mendefinisikan tab_typsebagai tipe koleksi dasar, dan dalam kasus a VARCHAR, ini akan menjadi contoh:

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

Juga sebagai koreksi atas jawaban dari @a_horse_with_no_name pada situasi multi kolom, di mana Anda mungkin ingin menggabungkan masih pada kolom ketiga (atau lebih):

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;

Jika Anda membiarkan kondisi rn = 1sebagai tempat untuk kueri, Anda akan menggabungkan kolom lain dengan tidak benar.


0

Sangat sederhana - gunakan dalam kueri Anda sub-kueri dengan pilihan yang berbeda:

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

Cara termudah untuk menangani beberapa listagg adalah dengan menggunakan 1 WITH (faktor subquery) per kolom yang berisi listagg kolom itu dari pilihan yang berbeda:

    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)

Pemberian yang mana:

col1  col2List  col3List
1     2,3,4,5   3,4,6
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.