2009-06-22 8 views
2

ich eine Oracle-Tabelle, die wie folgt aussieht:Oracle SQL Query Statistik Zusammengefasst mit GROUP mit Daten BY

ID BATCH STATUS 
1 1  0 
2 1  0 
3 1  1 
4 2  0 

Das heißt, ID ist der Primärschlüssel, gibt es mehrere Zeilen für jeden sein "Batch", und jede Zeile hat einen Statuscode in der Spalte STATUS. Es gibt eine Reihe anderer Spalten, aber das sind die wichtigen.

Ich brauche eine Abfrage zu schreiben, die den Statuscodes für jeden Charge zusammenfassen werden; gibt es drei mögliche Werte, die in der Spalte Status, 0, 1 und 2, und ich möchte Ausgabe, die etwa wie folgt aussieht gehen kann:

BATCH STATUS0 STATUS1 STATUS2 
1  2  1  0 
2  1  0  0 

Diese Zahlen würden zählt; für Batch-1 gibt es

  • 2 Datensätze, bei denen STATUS-0
  • 1 gesetzt Datensatz, in dem STATUS-1 gesetzt ist, und
  • keine Aufzeichnungen, wo STATUS-0 gesetzt .

Für Batch-2 gibt es

  • 1 Datensatz wo STATUS-0 gesetzt und
  • keine Aufzeichnungen, wo STATUS-1 gesetzt ist oder 2.

Gibt es eine Möglichkeit, dass ich dies in einer Abfrage tun, ohne muss die Abfrage für jeden Statuscode neu geschrieben werden? dh ich kann leicht eine Abfrage so schreiben, und führen Sie es dreimal:

SELECT batch, COUNT(status) 
FROM table 
WHERE status = 0 
GROUP BY batch 

ich laufen konnte, führen Sie es dann wieder in den Status = 1, und wieder in den Status = 2, aber ich bin die Hoffnung, Mach es in einer Abfrage.

Wenn es einen Unterschied macht, abgesehen von der STATUS Spalte gibt es andere Spalte, die ich auf die gleiche Weise zusammenfassen möchten - ein weiterer Grund, dass ich nicht wollen, SELECT-Anweisung nach SELECT haben auszuführen Aussage und fusionieren alle Ergebnisse.

Antwort

6
select batch 
,  count(case when status=1 then 1 end) status1 
,  count(case when status=2 then 1 end) status2 
,  count(case when status=3 then 1 end) status3 
from table 
group by batch; 

Dies wird oft eine „Pivot“ Abfrage genannt, und ich habe einen Artikel darüber geschrieben, wie diese Abfragen dynamisch on my blog zu erzeugen.

Version DECODE (Oracle-spezifische, aber weniger ausführlich) mit:

select batch 
,  count(decode(status,1,1)) status1 
,  count(decode(status,2,1)) status2 
,  count(decode(status,3,1)) status3 
from table 
group by batch; 
+0

Danke dafür! – imiric

1
select batch, 
sum(select case when status = 0 then 1 else 0 end) status0, 
sum(select case when status = 1 then 1 else 0 end) status1, 
sum(select case when status = 2 then 1 else 0 end) status2 
from table 
group by batch 
1
select batch, 
sum((decode(status,0,1,0)) status0, 
sum((decode(status,1,1,0)) status1, 
sum((decode(status,2,1,0)) status2, 
from table 
group by batch 
+1

Ein Vorteil von COUNT ist, dass es nie NULL zurückgibt, so dass Ihr DECODE noch kompakter sein kann: count ((decode (status, 0,1)) status0 –

1

OP fragt, ob es einen Performance-Vorteil von einem Ansatz ist (SUM) über den anderen (COUNT).Das Ausführen eines einfachen Tests für eine Tabelle mit 26 KB-Zeilen zeigt, dass der COUNT-Ansatz wesentlich schneller ist. YMMV.

DECLARE 
    CURSOR B IS 
    select batch_id 
     FROM batch 
     WHERE ROWNUM < 2000; 

    v_t1 NUMBER; 
    v_t2 NUMBER; 
    v_c1 NUMBER; 
    v_c2 NUMBER; 
    v_opn INTEGER; 
    v_cls INTEGER; 
    v_btc VARCHAR2(100); 
BEGIN 
-- Loop using SUM 
    v_t1 := dbms_utility.get_time; 
    v_c1 := dbms_utility.get_cpu_time; 
    FOR R IN B LOOP 
    FOR R2 IN (SELECT batch_type_code 
        , SUM(decode(batch_status_code, 'CLOSED', 1, 0)) closed 
        , SUM(decode(batch_status_code, 'OPEN', 1, 0)) OPEN 
        , SUM(decode(batch_status_code, 'REWORK', 1, 0)) rework 
        FROM batch 
       GROUP BY batch_type_code) LOOP 
     v_opn := R2.open; 
     v_cls := R2.closed; 
    END LOOP; 
    END LOOP; 
    v_t2 := dbms_utility.get_time; 
    v_c2 := dbms_utility.get_cpu_time; 
    dbms_output.put_line('For loop using SUM:'); 
    dbms_output.put_line('CPU seconds used: '||(v_c2 - v_c1)/100); 
    dbms_output.put_line('Elapsed time: '||(v_t2 - v_t1)/100); 

-- Loop using COUNT 
    v_t1 := dbms_utility.get_time; 
    v_c1 := dbms_utility.get_cpu_time; 
    FOR R IN B LOOP 
    FOR R2 IN (SELECT batch_type_code 
        , COUNT(CASE WHEN batch_status_code = 'CLOSED' THEN 1 END) closed 
        , COUNT(CASE WHEN batch_status_code = 'OPEN' THEN 1 END) OPEN 
        , COUNT(CASE WHEN batch_status_code = 'REWORK' THEN 1 END) rework 
        FROM batch 
       GROUP BY batch_type_code) LOOP 
     v_opn := R2.open; 
     v_cls := R2.closed; 
    END LOOP; 
    END LOOP; 
    v_t2 := dbms_utility.get_time; 
    v_c2 := dbms_utility.get_cpu_time; 
    dbms_output.put_line('For loop using COUNT:'); 
    dbms_output.put_line('CPU seconds used: '||(v_c2 - v_c1)/100); 
    dbms_output.put_line('Elapsed time: '||(v_t2 - v_t1)/100); 
END; 
/

Dies ergab die folgende Ausgabe:

For loop using SUM: 
CPU seconds used: 40 
Elapsed time: 40.09 
For loop using COUNT: 
CPU seconds used: 33.26 
Elapsed time: 33.34 

ich den Test ein paar Mal alle Auswirkungen des Caching zu beseitigen wiederholt. Ich habe auch die Select-Anweisungen ausgetauscht. Die Ergebnisse waren durchweg ähnlich.

EDIT: Dies ist der gleiche Test-Kabelbaum, den ich verwendet habe, um a similar question mit zu beantworten.