2009-07-13 17 views
31

Ich habe eine Tabelle, die wie folgt aussieht:Gibt es eine Oracle SQL-Abfrage, die mehrere Zeilen in einer Zeile aggregiert?

A 1 
A 2 
B 1 
B 2 

Und ich möchte eine Ergebnismenge erzeugen, die wie folgt aussieht:

A 1 2 
B 1 2 

Gibt es eine SQL-Anweisung, die dies tun wird? Ich benutze Oracle.

Verwandte Fragen:

+0

http://stackoverflow.com/questions/492563/oracle-combine-multiple-results-in-a-subquery-into-a-single-comma-separated-valu – derobert

+0

Siehe ähnliche Frage [Beratung -using-pivot-table-in-oracle] (http://stackoverflow.com/questions/365238/advice-using-pivot-table-in-oracle) –

Antwort

28

Es hängt von der von Ihnen verwendeten Oracle-Version ab. Wenn es die wm_concat() Funktion unterstützt, dann können Sie einfach so etwas tun:

SELECT field1, wm_concat(field2) FROM YourTable GROUP BY field2; 

wm_concat() im Grunde nur wie group_concat() in MySQL funktioniert. Es kann nicht dokumentiert werden, also feuern Sie die alten Sqlplus und sehen Sie, ob es da ist.

Wenn es ist nicht dort, dann möchten Sie etwas ähnliches implementieren. Sie können einige Anweisungen dazu in der string aggregation page bei oracle-base.com finden.

+7

Beachten Sie, dass wmsys.wm_concat nicht dokumentiert und nicht unterstützt wird. – turbanoff

4

Probieren Sie etwas wie:

SELECT 
    field1, 
    RTRIM(REPLACE(REPLACE(XMLAgg(XMLElement("x", field2) ORDER BY field2), '<x>'), '</x>', ' ')) AS field2s 
    FROM yourTable 
    GROUP BY field1 

Frei durch eine Antwort inspiriert found in this Oracle forum.

EDIT: diese Lösung erwies sich als sehr Ressourcen intensiv mit Anfragen so etwas wie 10 Reihen beteiligt sind. Am Ende habe ich das durch benutzerdefinierte Aggregatfunktionen wie suggested by John ersetzt.

9

In Oracle 10g+:

SELECT * 
FROM (
     SELECT * 
     FROM mytable 
     MODEL 
     PARTITION BY 
       (grouper) 
     DIMENSION BY 
       (ROW_NUMBER() OVER (PARTITION BY grouper ORDER BY id) AS rn) 
     MEASURES 
       (val, val AS group_concat, 0 AS mark) 
     RULES SEQUENTIAL ORDER (
       group_concat[rn > 1] ORDER BY rn = group_concat[CV() - 1] || ', ' || val[CV()], 
       mark[ANY] ORDER BY rn = PRESENTV(mark[CV() + 1], 0, 1) 
       ) 
     ) 
WHERE mark = 1 
ORDER BY 
     grouper 

Lesen Sie diesen Artikel in meinem Blog für Erklärungen:

+0

Sie sind völlig abhängig von mytable nach rn sortiert. Wenn es nicht sortiert ist, erhalten Sie eine "ORA-32637: Selbst zyklische Regel in sequentieller Reihenfolge MODEL". Diese Variante ist also fehlerhaft. Vielleicht möchten Sie eine andere Variante der Modellklausel in meinem Blog lesen: http://rwijk.blogspot.com/2008/05/string-aggregation-with-model-clause.html –

+0

@Rob: 'rn' hier ist nur ein Alias für 'ROW_NUMBER()'. Ich habe es von meinem Artikel kopiert (der ein 'CTE' als Zeilenquelle verwendet), aber es nicht in die Tabelle eingefügt. Danke fürs bemerken. – Quassnoi

+0

Ja, ich weiß. Wenn Sie jedoch beispielsweise eine andere analytische Funktion mit einer anderen Reihenfolge verwendet haben und die begleitende WINDOW SORT-Operation zuletzt ausgeführt wird, erhalten Sie den oben genannten Fehler. Sie müssen also die geordnete Regelauswertung verwenden, damit diese Idee funktioniert. –

21

Ziemlich altes Thema, aber es könnte anderen helfen, da Oracle in der verbesserten Zwischenzeit.

Die LISTAGG Funktion ist das, was Sie suchen (in 11g mindestens)

+1

Für ein vollständiges Syntaxbeispiel "LISTAGG (name, ',') WITHIN GROUP (ORDER BY name)". http://docs.oracle.com/cd/E11882_01/server.112/e10592/functions089.htm –

+0

Dies sollte die Antwort sein, danke sehr hilfreich! Ich wünschte nur, es hätte eine eindeutige Option. – beiller

+2

wählen Sie distinct c1, listagg (c2, '') innerhalb der Gruppe (nach c2) über (Partition nach c1) als c2_list aus Tabelle; –

2
SELECT a , COLLECT(b) FROM foo GROUP BY a 

sehr nützlich, wenn in PL/SQL verwendet - kann in einer benutzerdefinierten Sammlung gegossen werden.

+1

Als ich es in einer 11g-Datenbank versuchte, wurde CAST benötigt. Aber ich gab dir +1, weil ich nicht wusste, dass die COLLECT-Funktion existierte - Oracle fügt so viele Funktionen hinzu, dass es unmöglich ist, mitzuhalten. – redcayuga

4

Wenn Sie 10g erhalten haben, dann müssen Sie die Funktion durch unter:

CREATE OR REPLACE FUNCTION get_separated_value (input_val in number) 
    RETURN VARCHAR2 
IS 
    return_text VARCHAR2(10000) := NULL; 
BEGIN 
    FOR x IN (SELECT col2 FROM table_name WHERE col1 = input_val) LOOP 
    return_text := return_text || ' ' || x.col2 ; 
    END LOOP; 
    RETURN return_text; 
END; 
/

So können Sie tun:

select col1, get_separated_value(col1) from table_name 

Fiddle here

Wenn Sie haben, Orakel 11g, können Sie listagg verwenden:

SELECT 
    age, 
    LISTAGG(name, ' ') WITHIN GROUP (ORDER BY name) "names" 
FROM table_x 
GROUP BY age 

Fiddle here for Listagg