Oracle SQL Query для суммирования статистики с использованием GROUP BY - PullRequest
2 голосов
/ 22 июня 2009

У меня есть таблица Oracle с данными, которые выглядят так:

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

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

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

Ответы [ 4 ]

6 голосов
/ 22 июня 2009
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;
1 голос
/ 23 июня 2009

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

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

РЕДАКТИРОВАТЬ: это тот же тестовый комплект, который я использовал для ответа на аналогичный вопрос с.

1 голос
/ 22 июня 2009
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 голос
/ 22 июня 2009
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
...