SQL Query untuk menggabungkan nilai kolom dari beberapa baris di Oracle


169

Apakah mungkin untuk membangun SQL untuk menggabungkan nilai kolom dari beberapa baris?

Berikut ini adalah contohnya:

Tabel A

PID
SEBUAH
B
C

Tabel B

PID SEQ Desc

A 1 Have
A 2 a nice
3 hari.
B 1 Kerja Bagus.
C 1 Ya
C 2 kita bisa 
C 3 lakukan 
C 4 pekerjaan ini!

Output dari SQL seharusnya -

PID Desc
Semoga harimu menyenangkan.
B Kerja Bagus.
C Ya kita bisa melakukan pekerjaan ini!

Jadi pada dasarnya kolom Desc untuk tabel out put adalah gabungan dari nilai SEQ dari Tabel B?

Adakah bantuan dengan SQL?



Silakan lihat solusi ini . Ini akan bermanfaat bagi Anda.
Jineesh Uvantavida

Jawaban:


237

Ada beberapa cara tergantung pada versi apa yang Anda miliki - lihat dokumentasi oracle tentang teknik agregasi string . Yang sangat umum adalah menggunakan LISTAGG:

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

Kemudian bergabunglah untuk Amemilih yang pidsAnda inginkan.

Catatan: Di luar kotak, LISTAGGhanya berfungsi dengan benar dengan VARCHAR2kolom.


2
menggunakan wm_concat () untuk Oracle 10g menyatukan teks dalam urutan naik dari urutan nomor yang dibatasi oleh koma, dapatkah kita membuat turun dibatasi oleh sesuatu yang lain?
jagamot

19

Ada juga XMLAGGfungsi, yang berfungsi pada versi sebelum 11.2. Karena WM_CONCATtidak terdokumentasi dan tidak didukung oleh Oracle , disarankan untuk tidak menggunakannya dalam sistem produksi.

Dengan XMLAGGAnda dapat melakukan hal berikut:

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

Apa yang dilakukan adalah

  • letakkan nilai enamekolom (disatukan dengan koma) dari employee_namestabel dalam elemen xml (dengan tag E)
  • ekstrak teks ini
  • mengagregasi xml (menyatukannya)
  • sebut kolom yang dihasilkan "Hasil"

XMLAGG bekerja pada Oracle 12.2. Selain itu, XLMAGG memungkinkan untuk merangkai string yang sangat panjang yang LISTAGG mungkin tidak karena panjangnya yang terakhir.
Marco

13

Dengan klausa model 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.

Saya menulis tentang ini di sini . Dan jika Anda mengikuti tautan ke utas-OTN Anda akan menemukan lebih banyak lagi, termasuk perbandingan kinerja.



8

Seperti yang disarankan sebagian besar jawaban, LISTAGGadalah pilihan yang jelas. Namun, satu aspek yang mengganggu LISTAGGadalah bahwa jika panjang total string gabungan melebihi 4000 karakter (batas untuk VARCHAR2dalam SQL), kesalahan di bawah ini dilemparkan, yang sulit untuk dikelola dalam versi Oracle hingga 12.1

ORA-01489: hasil penggabungan string terlalu panjang

Fitur baru yang ditambahkan dalam 12cR2 adalah ON OVERFLOWklausa LISTAGG. Kueri termasuk klausa 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 output hingga 4000 karakter tetapi tidak akan membuang ORA-01489kesalahan.

Ini adalah beberapa opsi tambahan ON OVERFLOWklausa:

  • ON OVERFLOW TRUNCATE 'Contd..' : Ini akan ditampilkan 'Contd..'di akhir string (Default is ...)
  • ON OVERFLOW TRUNCATE '' : Ini akan menampilkan 4000 karakter tanpa string yang berakhir.
  • ON OVERFLOW TRUNCATE WITH COUNT: Ini akan menampilkan jumlah total karakter pada bagian akhir setelah karakter yang diakhiri. Misalnya: - ' ...(5512)'
  • ON OVERFLOW ERROR: Jika Anda mengharapkan LISTAGGgagal dengan ORA-01489kesalahan (Yang merupakan standarnya sih).

6

Bagi mereka yang harus menyelesaikan masalah ini menggunakan Oracle 9i (atau lebih awal), Anda mungkin perlu menggunakan SYS_CONNECT_BY_PATH, karena LISTAGG tidak tersedia.

Untuk menjawab OP, kueri berikut akan menampilkan PID dari Tabel A dan menggabungkan semua kolom DESC dari Tabel 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;

Mungkin juga ada contoh di mana kunci dan nilai semua terkandung dalam satu tabel. Kueri berikut dapat digunakan di mana tidak ada Tabel A, dan hanya Tabel B yang ada:

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;

Semua nilai dapat disusun ulang sesuai keinginan. Deskripsi yang digabungkan secara individu dapat disusun kembali dalam klausa PARTITION BY, dan daftar PID dapat disusun ulang dalam klausa ORDER BY final.


Bergantian: mungkin ada saat-saat ketika Anda ingin menggabungkan semua nilai dari seluruh tabel menjadi satu baris.

Gagasan utama di sini adalah menggunakan nilai artifisial untuk kelompok deskripsi yang akan digabungkan.

Dalam kueri berikut, string konstan '1' digunakan, tetapi nilai apa pun akan berfungsi:

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;

Deskripsi gabungan individu dapat disusun kembali dalam klausa PARTITION BY.

Beberapa jawaban lain di halaman ini juga menyebutkan referensi yang sangat membantu ini: https://oracle-base.com/articles/misc/string-aggregation-techniques


3
  1. LISTAGG memberikan kinerja terbaik jika menyortir adalah suatu keharusan (00: 00: 05.85)

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

  2. COLLECT memberikan kinerja terbaik jika penyortiran tidak diperlukan (00: 00: 02.90):

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

  3. KUMPULKAN dengan pemesanan sedikit lebih lambat (00: 00: 07.08):

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

Semua teknik lainnya lebih lambat.


1
Akan sangat membantu untuk menguraikan jawaban Anda.
Jon Surrell

John, aku tidak ingin mengulang dari artikel tetapi singkatnya ini adalah hasilnya: 1. LISTAGG memberikan kinerja terbaik jika menyortir adalah suatu keharusan (00: 00: 05.85) 2. MENGUMPULKAN memberikan kinerja terbaik jika menyortir tidak dibutuhkan (00: 00: 02.90): SELECT pid, TO_STRING (CAST (COLLECT (Desc) AS varchar2_ntt)) AS Vals DARI B GROUP DENGAN pid; 3. KUMPULKAN dengan pemesanan sedikit lebih lambat (00: 00: 07.08): SELECT pid, TO_STRING (CAST (KUMPULKAN (KETERANGAN ORDER DENGAN KETERANGAN) SEBAGAI Varchar2_ntt)) AS Vals FROM B GROUP BY pid; Semua teknik lainnya lebih lambat.
Misho

1
Anda bisa mengedit jawaban Anda untuk memasukkan informasi yang relevan.
Jon Surrell

Saya terlambat mengedit dan itu sebabnya saya menambahkannya lagi. Maaf saya baru di sini dan baru mulai memahami itu.
Misho

1

Sebelum Anda menjalankan kueri pemilihan, jalankan ini:

SET SERVEROUT ON SIZE 6000

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

-1

Coba kode ini:

 SELECT XMLAGG(XMLELEMENT(E,fieldname||',')).EXTRACT('//text()') "FieldNames"
    FROM FIELD_MASTER
    WHERE FIELD_ID > 10 AND FIELD_AREA != 'NEBRASKA';

-3

Di pilih tempat Anda ingin rangkaian Anda, panggil fungsi SQL.

Sebagai contoh:

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

Kemudian untuk fungsi 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

Sintaks Function Header mungkin salah, tetapi prinsipnya berhasil.


Ini tidak valid untuk Oracle
a_horse_with_no_name
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.