Query Oracle SQL per riepilogare le statistiche, utilizzando GROUP BY
Domanda
Ho una tabella Oracle con dati che assomigliano a questo:
ID BATCH STATUS
1 1 0
2 1 0
3 1 1
4 2 0
Cioè, ID è la chiave primaria, ci saranno più righe per ogni " batch, " e ogni riga avrà un codice di stato nella colonna STATO . Ci sono un sacco di altre colonne, ma queste sono importanti.
Devo scrivere una query che riassuma i codici di stato per ciascun batch ; ci sono tre possibili valori che possono andare nella colonna STATUS, 0, 1 e 2, e vorrei un output simile a questo:
BATCH STATUS0 STATUS1 STATUS2
1 2 1 0
2 1 0 0
Quei numeri sarebbero conteggi; per il lotto 1, ci sono
- 2 record in cui STATO è impostato su 0
- 1 record in cui STATUS è impostato su 1 e
- nessun record in cui STATO è impostato su 0.
Per il lotto 2, c'è
- 1 record in cui STATUS è impostato su 0 e
- nessun record in cui STATO è impostato su 1 o 2.
Esiste un modo per farlo in una query, senza dover riscrivere la query per ciascun codice di stato? cioè posso facilmente scrivere una query come questa ed eseguirla tre volte:
SELECT batch, COUNT(status)
FROM table
WHERE status = 0
GROUP BY batch
Potrei eseguirlo, quindi eseguirlo di nuovo dove status = 1, e ancora dove status = 2, ma spero di farlo in una query.
Se fa la differenza, oltre alla colonna STATUS c'è un'altra colonna che potrei voler riassumere allo stesso modo - un'altra ragione per cui non lo faccio desidera eseguire l'istruzione SELECT dopo l'istruzione SELECT e riunire tutti i risultati.
Soluzione
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;
Questo è spesso chiamato " pivot " query e ho scritto un articolo su come generare dinamicamente queste query sul mio blog .
Versione che utilizza DECODE (specifico per Oracle ma meno dettagliato):
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;
Altri suggerimenti
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 chiede se ci sono vantaggi in termini di prestazioni di un approccio (SUM) rispetto all'altro (COUNT). L'esecuzione di un test semplice su una tabella con 26K righe mostra che l'approccio COUNT è significativamente più veloce. 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;
/
Ciò ha prodotto il seguente output:
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
Ho ripetuto il test un paio di volte per eliminare gli effetti della cache. Ho anche scambiato le dichiarazioni selezionate. I risultati sono stati simili su tutta la linea.
EDIT: questo è lo stesso cablaggio di prova che ho usato per rispondere a una domanda simile con.