Oracle'da birden çok satırdan sütun değerlerini birleştirmek için SQL Sorgusu


169

Birden çok satırdaki sütun değerlerini birleştirmek için SQL oluşturmak mümkün müdür?

Aşağıda bir örnek verilmiştir:

Tablo A

PID
bir
B
C

Tablo B

PID SEQ Desc

A 1 Var
A 2 güzel
3 gün.
B 1 Güzel Çalışma.
C 1 Evet
C 2 yapabiliriz 
C 3 yapmak 
C 4 bu çalışma!

SQL çıktısı -

PID Desc
A İyi günler.
B Güzel Çalışma.
C Evet, bu işi yapabiliriz!

Yani temelde çıkış tablosu için Desc sütunu, Tablo B'deki SEQ değerlerinin bir birleşimidir?

SQL ile ilgili herhangi bir yardım?



Lütfen bu çözüme bakın . Sizin için yararlı olacaktır.
Jineesh Uvantavida

Yanıtlar:


238

Hangi sürüme sahip olduğunuza bağlı olarak birkaç yol vardır - dize toplama teknikleri hakkındaki oracle belgelerine bakın . Çok yaygın olanı kullanmaktır LISTAGG:

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

Sonra istediğinizi Aseçmek için katılın pids.

Not: Kutudan çıkar çıkmaz LISTAGGyalnızca VARCHAR2sütunlarla doğru şekilde çalışır .


2
Oracle 10g için wm_concat () kullanmak, metni virgülle ayrılmış sıra numarasının artan sırasına göre birleştirir, başka bir şeyle sınırlandırılmış iniş yapabilir miyiz?
jagamot

19

XMLAGG11.2'den önceki sürümlerde çalışan bir işlev de vardır . Çünkü WM_CONCATedilir belgesiz ve Oracle tarafından desteklenmeyen , üretim sisteminde kullanmak için değil önerilir.

İle XMLAGGaşağıdakileri yapabilirsiniz:

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

Bunun yaptığı

  • enamesütunun değerlerini ( virgülle birleştirilen) employee_namestablodan xml öğesinde (E etiketi ile) yerleştirin
  • bunun metnini çıkar
  • xml'yi toplayın (bitiştirin)
  • elde edilen sütunu "Sonuç" olarak adlandır

XMLAGG, Oracle 12.2 üzerinde çalışır. Dahası, XLMAGG, LISTAGG'nin son uzunluklarından ötürü yapamayacağı çok uzun dizeleri konkatan etmeyi sağlar.
Marco

13

SQL model yan tümcesi ile:

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.

Bunu burada yazdım . OTN-iş parçacığının bağlantısını takip ederseniz, performans karşılaştırması da dahil olmak üzere daha fazlasını bulacaksınız.



8

Cevapların çoğunun önerdiği gibi LISTAGG, bariz bir seçenektir. Bununla birlikte, can sıkıcı bir özellik LISTAGG, birleştirilmiş dizenin toplam uzunluğu 4000 karakteri aşarsa ( VARCHAR2SQL için sınır ), 12.1'e kadar Oracle sürümlerinde yönetilmesi zor olan aşağıdaki hata atılır.

ORA-01489: dize birleştirme sonucu çok uzun

12cR2 eklenen yeni bir özellik olduğu ON OVERFLOWbir fıkra LISTAGG. Bu maddeyi içeren sorgu şöyle görünecektir:

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

Yukarıdakiler çıktıyı 4000 karakterle sınırlar, ancak ORA-01489hatayı atmaz.

Bunlar ON OVERFLOWfıkra ek seçeneklerinden bazılarıdır :

  • ON OVERFLOW TRUNCATE 'Contd..' : Bu 'Contd..', dizenin sonunda görüntülenecektir (Varsayılan değer ...)
  • ON OVERFLOW TRUNCATE '' : Bu, herhangi bir sonlandırma dizesi olmadan 4000 karakteri görüntüler.
  • ON OVERFLOW TRUNCATE WITH COUNT: Bu, sonlandırılan karakterlerin sonundaki toplam karakter sayısını görüntüler. Örnek: - ' ...(5512)'
  • ON OVERFLOW ERROR: Eğer hata LISTAGGile başarısız olacağını düşünüyorsanız ORA-01489(Bu zaten varsayılan).

6

Oracle 9i (veya öncesi) kullanarak bu sorunu çözmek isteyenler için, LISTAGG mevcut olmadığından muhtemelen SYS_CONNECT_BY_PATH kullanmanız gerekecektir.

OP'yi yanıtlamak için, aşağıdaki sorgu Tablo A'daki PID'yi görüntüler ve Tablo B'deki tüm DESC sütunlarını birleştirir:

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;

Anahtarların ve değerlerin hepsinin bir tabloda yer aldığı durumlar da olabilir. Aşağıdaki sorgu, Tablo A'nın olmadığı ve yalnızca Tablo B'nin bulunduğu yerlerde kullanılabilir:

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üm değerler istenildiği gibi yeniden sıralanabilir. Bireysel birleştirilmiş açıklamalar PARTITION BY yan tümcesinde yeniden sıralanabilir ve PID'lerin listesi son ORDER BY yan tümcesinde yeniden sıralanabilir.


Alternatif olarak: tüm tablodaki tüm değerleri tek bir satıra birleştirmek istediğiniz zamanlar olabilir.

Buradaki ana fikir, tanımlanacak grup grubu için yapay bir değer kullanmaktır.

Aşağıdaki sorguda, '1' sabit dizesi kullanılır, ancak herhangi bir değer çalışır:

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;

Bireysel birleştirilmiş açıklamalar PARTITION BY yan tümcesinde yeniden sıralanabilir.

Bu sayfadaki diğer bazı cevaplar da bu son derece yararlı referanstan bahsetti: https://oracle-base.com/articles/misc/string-aggregation-techniques


3
  1. Sıralama bir zorunluluksa LISTAGG en iyi performansı sunar (00: 00: 05.85)

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

  2. COLLECT, sıralama gerekli değilse en iyi performansı sağlar (00: 00: 02.90):

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

  3. SİPARİŞLE TOPLA biraz yavaştır (00: 00: 07.08):

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

Diğer tüm teknikler daha yavaştı.


1
Cevabınızı detaylandırmak faydalı olacaktır.
Jon Surrell

John, makaleden tekrarlamak istemedim ama kısacası sonuçlar: 1. LISTAGG, sıralama bir zorunluluksa en iyi performansı sunar (00: 00: 05.85) 2. COLLECT, sıralama değilse en iyi performansı sunar gerekli (00: 00: 02.90): SELECT pid, TO_STRING (CAST (COLLECT (Desc) AS varchar2_ntt)) B GRUPTA pid TARAFINDAN Vals OLARAK; 3. Siparişle birlikte TOPLAMA biraz yavaştır (00: 00: 07.08): PED SEÇ, TO_STRING (CAST (varchar2_ntt OLARAK TOPLAMA (Desc ORDER BY Desc))) B GRUP TARAFINDAN Vals AS; Diğer tüm teknikler daha yavaştı.
Misho

1
Yanıtınızı yalnızca ilgili bilgileri içerecek şekilde düzenleyebilirsiniz.
Jon Surrell

Düzenlemeye çok geç kaldım ve bu yüzden tekrar ekledim. Üzgünüm burada yeniyim ve sadece asmaya başladım.
Misho

1

Seçme sorgusu çalıştırmadan önce şunu çalıştırın:

SET SERVEROUT ON SIZE 6000

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

-1

Bu kodu deneyin:

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

-3

Birleştirme işleminizi istediğiniz yerde, bir SQL işlevini çağırın.

Örneğin:

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

Sonra SQL işlevi için:

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

İşlev Başlığı sözdizimi yanlış olabilir, ancak ilke çalışır.


Bu, Oracle için geçerlidir
a_horse_with_no_name
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.