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.

È stato utile?

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.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top