Запрос Oracle SQL для обобщения статистики с использованием GROUP BY
Вопрос
У меня есть таблица Oracle с данными, которые выглядят следующим образом:
ID BATCH STATUS
1 1 0
2 1 0
3 1 1
4 2 0
Это, ID является первичным ключом, для каждого "пакета" будет несколько строк, и каждая строка будет иметь код состояния в Статус колонна.Есть куча других колонок, но эти - самые важные.
Мне нужно написать запрос, который будет суммировать коды состояния для каждого партия;есть три возможных значения, которые могут быть указаны в столбце СОСТОЯНИЯ: 0, 1 и 2, и я хотел бы, чтобы выходные данные выглядели примерно так:
BATCH STATUS0 STATUS1 STATUS2
1 2 1 0
2 1 0 0
Эти цифры были бы подсчетами;для пакета 1 существуют
- 2 записи , где Статус имеет значение 0
- 1 запишите, где Статус имеет значение 1, и
- нет записей , где Статус имеет значение 0.
Для пакета 2 существует
- 1 запишите, где Статус имеет значение 0, и
- нет записей , где Статус имеет значение 1 или 2.
Есть ли способ, которым я могу сделать это в одном запросе, без необходимости переписывать запрос для каждого кода состояния?т. е.Я могу легко написать подобный запрос и выполнить его три раза:
SELECT batch, COUNT(status)
FROM table
WHERE status = 0
GROUP BY batch
Я мог бы запустить это, затем запустить его снова, где status = 1, и снова, где status = 2, но я надеюсь сделать это одним запросом.
Если это имеет значение, помимо Статус колонка есть другой столбец, который я, возможно, захочу суммировать таким же образом - еще одна причина, по которой я не хочу выполнять оператор SELECT после оператора SELECT и объединять все результаты.
Решение
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;
Это часто называют "сводным" запросом, и я написал статью о том, как динамически генерировать эти запросы в моем блоге.
Версия, использующая 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;
Другие советы
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 спрашивает, есть ли какое-либо преимущество в производительности одного подхода (SUM) по сравнению с другим (COUNT).Выполнение простого теста на таблице с 26 тысячами строк показывает, что подход с подсчетом значительно быстрее.ИММВ.
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
Я повторил тест пару раз, чтобы устранить любые эффекты кэширования.Я также поменял местами инструкции select .Результаты были одинаковыми по всем направлениям.
Редактировать:это тот же самый тестовый жгут, который я использовал для ответа аналогичный вопрос с помощью.