Pregunta

Tengo una tabla Oracle con datos que se ven así:

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

Es decir, ID es la clave principal, habrá varias filas para cada " lote " y cada fila tendrá un código de estado en la columna ESTADO . Hay muchas otras columnas, pero estas son las importantes.

Necesito escribir una consulta que resuma los códigos de estado para cada lote ; Hay tres valores posibles que pueden ir en la columna ESTADO, 0, 1 y 2, y me gustaría una salida similar a esta:

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

Esos números serían cuentas; para el lote 1, hay

  • 2 registros donde STATUS se establece en 0
  • 1 registro donde STATUS se establece en 1 y
  • no hay registros donde STATUS se establece en 0.

Para el lote 2, hay

  • 1 registro donde STATUS se establece en 0 y
  • no hay registros donde STATUS se establece en 1 o 2.

¿Hay alguna manera de hacer esto en una consulta, sin tener que volver a escribir la consulta para cada código de estado? es decir, puedo escribir fácilmente una consulta como esta y ejecutarla tres veces:

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

Podría ejecutar eso, luego ejecutarlo nuevamente donde status = 1, y nuevamente donde status = 2, pero espero hacerlo en una consulta.

Si hace la diferencia, aparte de la columna ESTADO hay una columna otra que podría resumir de la misma manera, otra razón por la que no desea tener que ejecutar la instrucción SELECT después de la instrucción SELECT y amalgamar todos los resultados.

¿Fue útil?

Solución

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;

Esto a menudo se llama " pivote " consulta, y he escrito un artículo sobre cómo generar estas consultas dinámicamente en mi blog .

Versión que usa DECODE (específica de Oracle pero menos detallada):

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;

Otros consejos

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 pregunta si hay algún beneficio de rendimiento de un enfoque (SUM) sobre el otro (COUNT). La ejecución de una prueba simple en una tabla con 26K filas muestra que el enfoque COUNT es significativamente más rápido. 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;
/

Esto produjo el siguiente resultado:

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

Repetí la prueba un par de veces para eliminar los efectos del almacenamiento en caché. También cambié las declaraciones select. Los resultados fueron similares en todos los ámbitos.

EDITAR: este es el mismo arnés de prueba que usé para responder una pregunta similar con.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top