Oracle SQL Query Statistik Zusammengefasst mit GROUP BY
Frage
Ich habe eine Oracle-Tabelle mit Daten, die wie folgt aussieht:
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 wird ein Statuscode im Status Spalte haben. Es gibt eine Reihe von anderen Spalten, aber diese sind die wichtigsten.
Ich brauche eine Abfrage zu schreiben, die die Statuscodes für jeden Charge zusammenfassen ; 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, in denen Status 0 gesetzt
- 1 Datensatz, in dem Status auf 1 gesetzt ist, und
- keine Datensätze, in denen Status auf 0 gesetzt
Für Batch-2 gibt es
- 1 Datensatz, in dem Status auf 0 gesetzt ist, und
- keine Datensätze, in denen Status auf 1 gesetzt ist oder 2.
Gibt es eine Möglichkeit, dass ich dies in einer Abfrage zu tun, ohne die Abfrage für jeden Statuscode neu zu schreiben? das heißt ich eine Abfrage wie dies leicht schreiben kann, und es dreimal laufen:
SELECT batch, COUNT(status)
FROM table
WHERE status = 0
GROUP BY batch
ich laufen kann, ist es dann wieder laufen, wo Status = 1, und wieder in dem Status = 2, aber ich hoffe, es in einer Abfrage zu tun.
Wenn es einen Unterschied macht, abgesehen von dem Status Spalte gibt es andere Spalt, die ich auf die gleiche Weise zusammenfassen mag - einen weiteren Grund, dass ich nicht tun haben wollen SELECT-Anweisung nach SELECT-Anweisung auszuführen und alle Ergebnisse amalgamieren.
Lösung
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 ist oft eine „Pivot“ Abfrage genannt, und ich habe einen Artikel darüber geschrieben, wie diese Abfragen dynamisch generiert auf meinem Blog .
Version mit DECODE (Oracle-spezifischer, aber weniger ausführlich):
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;
Andere Tipps
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
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
OP fragt, ob es einen Performance-Vorteil von einem Ansatz ist (SUM) über den anderen (COUNT). einen einfältigen Test auf einer Tabelle mit 26K Reihen läuft, dass der COUNT Ansatz deutlich 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 wiederholte den Test ein paar Mal alle Auswirkungen des Caching zu beseitigen. Ich tauschte auch die Select-Anweisungen. Die Ergebnisse waren auf der ganzen Linie ähnlich.
EDIT: Dies ist der gleiche Test-Harnisch ich eine ähnliche Frage mit.