GROUP BYを使用した統計を要約するOracle SQLクエリ
質問
次のようなデータを持つOracleテーブルがあります:
ID BATCH STATUS
1 1 0
2 1 0
3 1 1
4 2 0
つまり、 ID が主キーです。「バッチ」ごとに複数の行があります。各行の[ STATUS ]列にステータスコードがあります。他にもたくさんの列がありますが、これらは重要な列です。
各バッチのステータスコードを要約するクエリを作成する必要があります。 STATUS列に入力できる値は0、1、2の3つです。次のような出力が必要です:
BATCH STATUS0 STATUS1 STATUS2
1 2 1 0
2 1 0 0
これらの数値はカウントです。バッチ1には
があります- STATUS が0に設定されている2つのレコード
- STATUS が1に設定されている1つのレコード
- STATUS が0に設定されているレコードはありません。
バッチ2には
があります- STATUS が0に設定されている1つのレコード、および
- STATUS が1または2に設定されているレコードはありません
各ステータスコードのクエリを書き直すことなく、1つのクエリでこれを実行できる方法はありますか?つまり、このようなクエリを簡単に記述し、3回実行できます:
SELECT batch, COUNT(status)
FROM table
WHERE status = 0
GROUP BY batch
それを実行してから、ステータス= 1で再度実行し、ステータス= 2で再び実行できますが、1つのクエリで実行したいと考えています。
違いがある場合は、 STATUS 列の他に、同じ方法で要約したい別の列があります。 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は、1つのアプローチ(SUM)が他のアプローチ(COUNT)よりもパフォーマンス上の利点があるかどうかを尋ねます。 26K行のテーブルでsimplemindedテストを実行すると、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
キャッシュの影響を排除するために、テストを数回繰り返しました。また、selectステートメントを交換しました。結果は全面的に同様でした。
編集:これは、同様の質問に答えるために使用したものと同じテストハーネスです。 と。