2009-06-22 8 views
2

У меня есть таблица Oracle с данными, который выглядит следующим образом:Oracle SQL Query Суммировать статистики, используя GROUP BY

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

То есть ID является первичным ключом, будет несколько строк для каждого «партия», и каждая строка будет иметь код состояния в столбце STATUS. Есть еще несколько столбцов, но они важны.

мне нужно написать запрос, который будет суммировать код статуса для каждой партии ; Есть три возможных значения, которые могут пойти в колонке STATUS, 0, 1, и 2, и я хотел бы вывод, который выглядит примерно так:

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

Эти цифры будут на счету; для партии 1, есть

  • 2 записей, где STATUS установлен в 0
  • 1 запись, где STATUS устанавливается в 1, и
  • нет записей, где STATUS устанавливается равным 0 .

для партии 2, есть

  • 1 запись, где STATUS устанавливается в 0, и
  • нет записей, где STATUS установлен в 1 или 2.

Есть ли способ, что я могу сделать это в одном запросе, без нужно переписать запрос для каждого кода состояния? то есть я могу легко написать запрос, как это, и запустить его в три раза:

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

я мог бежать, что, а затем запустить его снова, где состояние = 1, и снова где статус = 2, но я надеюсь сделайте это в одном запросе.

Если это имеет значение, в стороне от STATUS колонке есть другой столбец, который я мог бы суммировать таким же образом - еще одна причина, по которой я не хочу, чтобы выполнить SELECT, заявление после SELECT, заявление и объединить все результаты.

ответ

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; 

Это часто называют «шарнирный» запрос, и я написал статью о том, как создавать эти запросы динамически on my blog.

Версия с использованием DECODE (Oracle конкретных но менее многословным):

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

Спасибо за это! – 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

Преимущество COUNT заключается в том, что он никогда не возвращает NULL, поэтому ваш DECODE может быть еще более компактным: count ((decode (status, 0,1)) status0 –

1

OP спрашивает, есть ли бенефис одного подхода (SUM) над другой (COUNT).Запуск простого теста на таблицу с 26K строк показывает, что метод COUNT значительно быстрее. 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; 
/

Это дало следующий результат:

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 

я повторил тест несколько раз, чтобы устранить любые эффекты кэширования. Я также поменял выбранные утверждения. Результаты были одинаковыми по всем направлениям.

EDIT: это тот же испытательный жгут, который я использовал для ответа a similar question.