2009-07-13 14 views
31

Ho una tabella che assomiglia a questo:Esiste una query Oracle Oracle che aggrega più righe in una riga?

A 1 
A 2 
B 1 
B 2 

e voglio produrre un set di risultati che assomiglia a questo:

A 1 2 
B 1 2 

C'è un'istruzione SQL che farà questo? Sto usando Oracle.

domande correlate:

+0

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

+0

Vedere domanda simile consiglio [ -using-pivot-table-in-oracle] (http://stackoverflow.com/questions/365238/advice-using-pivot-table-in-oracle) –

risposta

28

Dipende dalla versione di Oracle che si sta utilizzando. Se supporta la funzione wm_concat(), allora si può semplicemente fare qualcosa di simile:

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

wm_concat() in pratica funziona come group_concat() in MySQL. Potrebbe non essere documentato, quindi licenzia il tuo sqlplus e vedi se è lì.

Se il numero non è, è necessario implementare qualcosa di equivalente. Puoi trovare alcune istruzioni su come farlo nello string aggregation page su oracle-base.com.

+7

Si noti che wmsys.wm_concat non è documentato e non è supportato. – turbanoff

4

provare qualcosa di simile:

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

Liberamente ispirato una risposta found in this Oracle forum.

EDIT: questa soluzione si è rivelata molto risorse intensive con le richieste che coinvolgono qualcosa come 10 righe. Ho finito per sostituirlo con funzioni di aggregazione personalizzate come suggested by John.

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 

si veda questo articolo nel mio blog per le spiegazioni:

+0

Sei totalmente dipendente da mytable che viene ordinato per rn. Se non è ordinato otterrai una "ORA-32637: regola ciclica autonoma in ordine sequenziale MODELLO". Quindi questa variante è bacata. Puoi leggere un'altra variante della clausola del modello sul mio blog: http://rwijk.blogspot.com/2008/05/string-aggregation-with-model-clause.html –

+0

@Rob: 'rn' qui è solo un alias per 'ROW_NUMBER()'. L'ho copiato dal mio articolo (che usava un 'CTE' come origine di righe) ma non lo metteva nella tabella. Grazie per averlo notato. – Quassnoi

+0

Sì, lo so. Ma se, per esempio, hai usato un'altra funzione analitica con un ordine diverso per, e che l'operazione WINDOW SORT di accompagnamento è stata eseguita per ultima, ottieni l'errore di cui sopra. Quindi devi ancora usare la valutazione della regola ordinata per far funzionare questa idea. –

21

Abbastanza vecchio argomento, ma potrebbe aiutare gli altri dal momento che Oracle ha migliorato nel tempo medio

La funzione LISTAGG è quello che state cercando (a 11g almeno)

+1

Per un esempio di sintassi completa, "LISTAGG (nome, ',') WITHIN GROUP (nome ORDER BY)". http://docs.oracle.com/cd/E11882_01/server.112/e10592/functions089.htm –

+0

Questa dovrebbe essere la risposta grazie mille aiuto! Vorrei solo che avesse un'opzione distinta. – beiller

+2

selezionare distinto c1, listagg (c2, '') all'interno di un gruppo (ordine di c2) sopra (partizione di c1) come c2_list dalla tabella; –

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

molto utile se usato in PL/SQL - può essere fusa ad una collezione definita dall'utente.

+1

Quando l'ho provato su un database 11g era richiesto il CAST. Ma ti ho dato +1 perché non sapevo che esistesse la funzione COLLECT - Oracle aggiunge così tante funzioni che è impossibile tenere il passo. – redcayuga

4

Se avete 10g, allora si deve passare attraverso la seguente funzione:

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; 
/

Quindi, si può fare come:

select col1, get_separated_value(col1) from table_name 

Fiddle here

Se avete ottenuto oracolo 11g, è possibile utilizzare listagg:

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

Fiddle here for Listagg

Problemi correlati