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?



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.


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".

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.



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

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


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.


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;

-1

Versuchen Sie diesen Code:

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

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


Dies ist ungültig für Oracle
a_horse_with_no_name
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.