SQL-Abfrage zu verketten Spaltenwerte aus mehreren Zeilen in Oracle
-
11-10-2019 - |
Frage
Ist es möglich, SQL zu verketten Spaltenwerte zu konstruieren aus mehrere Zeilen?
Im Folgenden ist ein Beispiel:
Tabelle A
PID A B C
Tabelle B
PID SEQ Desc A 1 Have A 2 a nice A 3 day. B 1 Nice Work. C 1 Yes C 2 we can C 3 do C 4 this work!
Ausgabe des SQL sollte -
PID Desc A Have a nice day. B Nice Work. C Yes we can do this work!
Also im Grunde die Beschr Spalte für out Put-Tabelle ist eine Verkettung der SEQ Werte aus Tabelle B?
Jede mögliche Hilfe bei der SQL?
Lösung
Es gibt ein paar Möglichkeiten, je nachdem, welche Version Sie haben - siehe Oracle-Dokumentation auf String Aggregationstechniken. Ein sehr häufig ist LISTAGG
:
SELECT pid, LISTAGG(Desc, ' ') WITHIN GROUP (ORDER BY seq) AS description
FROM B GROUP BY pid;
kommen dann zu A
die pids
herauszupicken Sie wollen.
. Hinweis: Aus dem Kasten heraus, LISTAGG
funktioniert nur korrekt mit VARCHAR2
Spalten
Andere Tipps
Es gibt auch eine XMLAGG
Funktion, die vor 11.2 auf Versionen funktioniert. Da WM_CONCAT
undokumentierte und nicht unterstützte von Oracle , wird empfohlen, nicht in Produktionssystem zu verwenden.
Mit XMLAGG
können Sie Folgendes tun:
SELECT XMLAGG(XMLELEMENT(E,ename||',')).EXTRACT('//text()') "Result"
FROM employee_names
Was das bedeutet ist
- stellt die Werte der
ename
Säule (mit einem Komma konkateniert) von deremployee_names
Tabelle in einem XML-Elemente (mit Tag E) - extrahiert den Text dieses
- aggregieren die XML (verketten it)
- rufen Sie die resultierende Spalte "Result"
Mit SQL-Modell-Klausel:
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 schrieb über dieses hier . Und wenn Sie den Link zum OTN-Thread folgen, werden Sie einige weitere, darunter ein Leistungsvergleich finden.
Die LISTAGG analytische Funktion wurde eingeführt Oracle 11g Release 2 , ist es sehr einfach zu aggregieren Saiten machen. Wenn Sie 11g Release 2 verwenden, sollten Sie diese Funktion für die Zeichenfolge Aggregation verwenden. Bitte beziehen Sie sich unter URL für weitere Informationen über die String-Verkettung.
http://www.oracle-base.com/articles/misc/ StringAggregationTechniques.php
Wie die meisten Antworten vorschlagen, ist LISTAGG
die offensichtliche Wahl. Doch mit LISTAGG
ein ärgerlicher Aspekt ist, dass, wenn die Gesamtlänge der verketteten Zeichenfolge überschreitet 4000 Zeichen (Limit für VARCHAR2
in SQL), wird die unten Fehler ausgelöst, die nur schwer in Oracle-Versionen bis 12.1
ORA-01489: Ergebnis der Stringverkettung zu lang
Ein neues Feature in 12cR2 hinzugefügt ist die ON OVERFLOW
Klausel LISTAGG
.
Die Abfrage dieser Klausel einschließlich würde wie folgt aussehen:
SELECT pid, LISTAGG(Desc, ' ' on overflow truncate) WITHIN GROUP (ORDER BY seq) AS desc
FROM B GROUP BY pid;
Das oben wird die Ausgabe auf 4000 Zeichen beschränken, sondern die ORA-01489
Fehler nicht werfen.
Dies sind nur einige der zusätzlichen Optionen von ON OVERFLOW
-Klausel:
-
ON OVERFLOW TRUNCATE 'Contd..'
: Dies wird angezeigt'Contd..'
an das Ende der Zeichenfolge (Standard ist...
) -
ON OVERFLOW TRUNCATE ''
: Das wird die 4000 Zeichen angezeigt werden ohne Abschluss String. -
ON OVERFLOW TRUNCATE WITH COUNT
: Damit erhöht sich die Anzeige Anzahl der Zeichen am Ende nach dem Abschlusszeichen. ZB: - '...(5512)
' -
ON OVERFLOW ERROR
: Wenn Sie dieLISTAGG
scheitern mit der erwartenORA-01489
Fehler (der Standard sowieso).
Für diejenigen, die dieses Problem mit Oracle 9i lösen muss (oder früher), werden Sie wahrscheinlich zu verwenden SYS_CONNECT_BY_PATH benötigen, da LISTAGG nicht verfügbar ist.
, um die OP zu beantworten, die folgende Abfrage wird die PID aus Tabelle A angezeigt werden und alle die DESC Spalten aus Tabelle B verketten:
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 gibt auch Fälle geben kann, in denen Schlüssel und Werte sind alle in einer Tabelle enthalten. Die folgende Abfrage kann verwendet werden, in denen es keine Tabelle A 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;
können alle Werte neu geordnet werden, wie gewünscht. Einzelne verketteten Beschreibungen können in der PARTITION BY-Klausel und die Liste der PIDs können nachbestellt werden in der endgültigen ORDER BY-Klausel erst nachbestellt werden.
Alternativ:. kann es vorkommen, dass Sie alle die Werte aus einer ganzen Tabelle in einer Zeile verketten möchten
Die Schlüsselidee ist hier ein künstliches Wert für die Gruppe von Beschreibungen mit verketteten werden.
In der folgenden Abfrage, der konstante String '1' verwendet wird, 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 verketteten können Beschreibungen in der PARTITION BY-Klausel erst nachbestellt werden.
Mehrere andere Antworten auf dieser Seite erwähnt haben auch diese äußerst hilfreich Referenz: https://oracle-base.com/articles/misc/string-aggregation-techniques
-
LISTAGG liefert die beste Leistung, wenn das Sortieren ist ein Muss (00: 00: 05.85)
SELECT pid, LISTAGG(Desc, ' ') WITHIN GROUP (ORDER BY seq) AS description FROM B GROUP BY pid;
-
liefert COLLECT die beste Leistung, wenn das Sortieren nicht benötigt wird (00: 00: 02.90):
SELECT pid, TO_STRING(CAST(COLLECT(Desc) AS varchar2_ntt)) AS Vals FROM B GROUP BY pid;
-
COLLECT mit der 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.
Bevor Sie eine SELECT-Abfrage ausführen, führen diese:
SET SERVEROUT ON SIZE 6000
SELECT XMLAGG(XMLELEMENT(E,SUPLR_SUPLR_ID||',')).EXTRACT('//text()') "SUPPLIER"
FROM SUPPLIERS;
ich die LISTAGG verwenden, aber diese Zeichenfolge für persisch String zurück!
meine Frage:
SELECT
listagg(DESCRIPTION,' , ') within group (order by DESCRIPTION)
FROM
B_CEREMONY
Ergebnis:
'A7'1 , ,4F
Bitte helfen Sie mir.
wow diese Lösung wird gearbeitet:
SELECT listagg(convert(DESCRIPTION, 'UTF8', 'AL16UTF16'),' , ') within group
(order by DESCRIPTION)
FROM B_CEREMONY;
Versuchen Sie diesen Code ein:
SELECT XMLAGG(XMLELEMENT(E,fieldname||',')).EXTRACT('//text()') "FieldNames"
FROM FIELD_MASTER
WHERE FIELD_ID > 10 AND FIELD_AREA != 'NEBRASKA';
In der Auswahl, wo Sie Ihre Verkettung möchten, rufen Sie eine SQL-Funktion.
Zum Beispiel:
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 Funktion Kopf Syntax könnte falsch sein, aber das Prinzip funktioniert.