SQL-Abfrage zum Verketten von Spaltenwerten aus mehreren Zeilen in Oracle

169

Wäre es möglich, SQL zu erstellen, um Spaltenwerte aus mehreren Zeilen zu verketten?

Das Folgende ist ein Beispiel:

Tabelle A.

PID
EIN
B.
C.

Tabelle B.

PID SEQ Desc

A 1 Haben
A 2 a schön
A 3 Tage.
B 1 Gute Arbeit.
C 1 Ja
C 2 können wir 
C 3 tun 
C 4 diese Arbeit!

Die Ausgabe des SQL sollte sein -

PID Desc
A Einen schönen Tag noch.
B Gute Arbeit.
C Ja, wir können diese Arbeit machen!

Die Desc-Spalte für die Ausgabetabelle ist also im Grunde eine Verkettung der SEQ-Werte aus Tabelle B?

Hilfe bei der SQL?

Jagamotte
quelle
Siehe zum Beispiel: halisway.blogspot.com/2006/08/…
Andomar
Bitte schauen Sie sich diese Lösung an . Es wird für Sie nützlich sein.
Jineesh Uvantavida

Antworten:

237

Abhängig von Ihrer Version gibt es verschiedene Möglichkeiten - siehe die Oracle-Dokumentation zu String-Aggregationstechniken . Eine sehr häufige ist zu verwenden LISTAGG:

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

Schließen Sie sich dann an A, um das pidsgewünschte auszuwählen .

Hinweis: Aus dem Kasten heraus , LISTAGGfunktioniert nur mit VARCHAR2Spalten.

Lou Franco
quelle
2
Wenn Sie wm_concat () für Oracle 10g verwenden, wird der Text in aufsteigender Reihenfolge der durch Kommas getrennten Sequenznummer verkettet. Können wir absteigende Zeichen durch etwas anderes trennen?
Jagamot
19

Es gibt auch eine XMLAGGFunktion, die bei Versionen vor 11.2 funktioniert. Da WM_CONCATes von Oracle nicht dokumentiert und nicht unterstützt wird , wird empfohlen, es nicht im Produktionssystem zu verwenden.

Mit XMLAGGkönnen Sie Folgendes tun:

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

Was dies tut, ist

  • Fügen Sie die Werte der enameSpalte (mit einem Komma verknüpft) aus der employee_namesTabelle in ein XML-Element ein (mit Tag E).
  • extrahieren Sie den Text davon
  • aggregiere die XML (verkette sie)
  • Nennen Sie die resultierende Spalte "Ergebnis".
Peter
quelle
XMLAGG funktioniert unter Oracle 12.2. Darüber hinaus ermöglicht XLMAGG das Konkatanisieren sehr langer Zeichenfolgen, die LISTAGG aufgrund ihrer endgültigen Länge möglicherweise nicht enthält.
Marco
13

Mit SQL-Modellklausel:

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.

Ich habe hier darüber geschrieben . Und wenn Sie dem Link zum OTN-Thread folgen, finden Sie weitere, einschließlich eines Leistungsvergleichs.

Rob van Wijk
quelle
10

Die Analysefunktion LISTAGG wurde in Oracle 11g Release 2 eingeführt , wodurch das Aggregieren von Zeichenfolgen sehr einfach ist. Wenn Sie 11g Release 2 verwenden, sollten Sie diese Funktion für die Zeichenfolgenaggregation verwenden. Weitere Informationen zur Verkettung von Zeichenfolgen finden Sie unter der folgenden URL.

http://www.oracle-base.com/articles/misc/StringAggregationTechniques.php

String-Verkettung

Ashish J.
quelle
8

Wie die meisten Antworten vermuten lassen, LISTAGGist dies die offensichtliche Option. Ein ärgerlicher Aspekt dabei LISTAGGist jedoch, dass VARCHAR2der folgende Fehler ausgegeben wird, wenn die Gesamtlänge der verketteten Zeichenfolge 4000 Zeichen überschreitet (Limit für in SQL), was in Oracle-Versionen bis 12.1 schwierig zu verwalten ist

ORA-01489: Ergebnis der Zeichenfolgenverkettung ist zu lang

Eine neue Funktion, die in 12cR2 hinzugefügt wurde, ist die ON OVERFLOWKlausel von LISTAGG. Die Abfrage mit dieser Klausel würde folgendermaßen aussehen:

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

Das Obige beschränkt die Ausgabe auf 4000 Zeichen, löst aber keinen ORA-01489Fehler aus.

Dies sind einige der zusätzlichen Optionen der ON OVERFLOWKlausel:

  • ON OVERFLOW TRUNCATE 'Contd..' : Dies wird 'Contd..'am Ende der Zeichenfolge angezeigt (Standard ist ...)
  • ON OVERFLOW TRUNCATE '' : Dadurch werden die 4000 Zeichen ohne Abschlusszeichenfolge angezeigt.
  • ON OVERFLOW TRUNCATE WITH COUNT: Hiermit wird die Gesamtzahl der Zeichen am Ende nach den abschließenden Zeichen angezeigt. ZB: - ' ...(5512)'
  • ON OVERFLOW ERROR: Wenn Sie erwarten LISTAGG, dass das mit dem ORA-01489Fehler fehlschlägt (was sowieso Standard ist).
Kaushik Nayak
quelle
6

Für diejenigen, die dieses Problem mit Oracle 9i (oder früher) lösen müssen, müssen Sie wahrscheinlich SYS_CONNECT_BY_PATH verwenden, da LISTAGG nicht verfügbar ist.

Um das OP zu beantworten, zeigt die folgende Abfrage die PID aus Tabelle A an und verkettet alle DESC-Spalten aus Tabelle 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;

Es kann auch Fälle geben, in denen Schlüssel und Werte in einer Tabelle enthalten sind. Die folgende Abfrage kann verwendet werden, wenn keine Tabelle A vorhanden ist und nur Tabelle B vorhanden ist:

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;

Alle Werte können nach Wunsch neu angeordnet werden. Einzelne verkettete Beschreibungen können in der PARTITION BY-Klausel neu angeordnet werden, und die Liste der PIDs kann in der endgültigen ORDER BY-Klausel neu angeordnet werden.


Alternativ: Es kann vorkommen, dass Sie alle Werte einer gesamten Tabelle in einer Zeile verketten möchten.

Die Schlüsselidee hierbei ist die Verwendung eines künstlichen Werts für die zu verkettende Gruppe von Beschreibungen.

In der folgenden Abfrage wird die konstante Zeichenfolge '1' verwendet, aber jeder Wert funktioniert:

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;

Einzelne verkettete Beschreibungen können in der PARTITION BY-Klausel neu angeordnet werden.

Einige andere Antworten auf dieser Seite haben diese äußerst hilfreiche Referenz ebenfalls erwähnt: https://oracle-base.com/articles/misc/string-aggregation-techniques

JonathanDavidArndt
quelle
3
  1. LISTAGG liefert die beste Leistung, wenn das Sortieren ein Muss ist (00: 00: 05.85)

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

  2. COLLECT liefert die beste Leistung, wenn keine Sortierung erforderlich ist (00: 00: 02.90):

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

  3. COLLECT mit Bestellung ist etwas langsamer (00: 00: 07.08):

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

Alle anderen Techniken waren langsamer.

Mischo
quelle
1
Es wäre hilfreich, auf Ihre Antwort einzugehen.
Jon Surrell
John, ich wollte den Artikel nicht wiederholen, aber kurz gesagt, dies sind die Ergebnisse: 1. LISTAGG liefert die beste Leistung, wenn das Sortieren ein Muss ist (00: 00: 05.85) 2. COLLECT liefert die beste Leistung, wenn das Sortieren nicht ist benötigt (00: 00: 02.90): SELECT pid, TO_STRING (CAST (COLLECT (Desc) AS varchar2_ntt)) AS Vals FROM B GROUP BY pid; 3. COLLECT mit Reihenfolge ist etwas langsamer (00: 00: 07.08): SELECT pid, TO_STRING (CAST (COLLECT (Desc ORDER BY Desc) AS varchar2_ntt)) AS Vals FROM B GROUP BY pid; Alle anderen Techniken waren langsamer.
Mischo
1
Sie können Ihre Antwort einfach bearbeiten, um relevante Informationen aufzunehmen.
Jon Surrell
Ich war zu spät in der Bearbeitung und deshalb habe ich es wieder hinzugefügt. Tut mir leid, dass ich neu hier bin und gerade erst anfange, den Dreh raus zu bekommen.
Mischo
1

Führen Sie Folgendes aus, bevor Sie eine Auswahlabfrage ausführen:

SET SERVEROUT ON SIZE 6000

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

Versuchen Sie diesen Code:

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

Rufen Sie in der Auswahl, wo Sie Ihre Verkettung wünschen, eine SQL-Funktion auf.

Beispielsweise:

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

Dann für die SQL-Funktion:

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

Die Syntax des Funktionsheaders ist möglicherweise falsch, aber das Prinzip funktioniert.

user5473005
quelle
Dies ist ungültig für Oracle
a_horse_with_no_name