Oracle の CASE式は、SQL 標準に準拠した条件分岐の仕組みです。IF ~ THEN ~ ELSE のようなロジックを SQL や PL/SQL の中に組み込めます。
CASE式は SELECT・WHERE・ORDER BY・GROUP BY・集計関数の中など、式(Expression)を書ける場所であればほぼどこでも使えます。単純な値の置き換えから、条件集計・動的ソート・PIVOT代替まで幅広く活用できます。
- 単純 CASE と検索 CASE の構文と使い分け
- SELECT / WHERE / ORDER BY / HAVING での CASE 活用
- SUM(CASE WHEN …)による条件集計(クロス集計)
- PIVOT を使わない行→列変換パターン
- CASE と NULL の関係・ELSE 省略時の注意
- PL/SQL CASE 文(IF-ELSIF との違い)
- Oracle 23ai: GROUP BY カラムエイリアス(CASE 式の簡略化)
CASE 式の 2 種類:単純 CASE と検索 CASE
| 種類 | 構文 | 得意なケース |
|---|---|---|
| 単純 CASE | CASE 列 WHEN 値1 THEN 結果1 ... END |
1列の値で分岐するとき(等値比較のみ) |
| 検索 CASE | CASE WHEN 条件1 THEN 結果1 ... END |
複数列・範囲・不等号・サブクエリなど複雑な条件 |
-- 単純 CASE: CASE 列名 WHEN 値 THEN 結果 ... ELSE デフォルト END
SELECT employee_id,
department_id,
CASE department_id
WHEN 10 THEN '総務部'
WHEN 20 THEN '人事部'
WHEN 30 THEN '購買部'
WHEN 50 THEN '出荷部'
ELSE 'その他' -- いずれにも該当しない場合
END AS dept_name
FROM employees
ORDER BY department_id;
-- 検索 CASE: CASE WHEN 条件 THEN 結果 ... ELSE デフォルト END
-- 範囲・不等号・複合条件・サブクエリなど任意の条件が使える
SELECT employee_id, last_name, salary,
CASE
WHEN salary >= 800000 THEN 'S(上位)'
WHEN salary >= 500000 THEN 'A(中上位)'
WHEN salary >= 300000 THEN 'B(中位)'
ELSE 'C(下位)'
END AS salary_grade,
-- 複数列を組み合わせた条件も可能
CASE
WHEN department_id = 50 AND salary < 300000 THEN '要確認'
WHEN manager_id IS NULL THEN 'トップ'
ELSE '一般'
END AS emp_flag
FROM employees;
ELSE 句を省略した場合、どの WHEN にもマッチしない行は NULL が返ります。意図しない NULL が混入しないように、明示的に ELSE '不明' や ELSE 0 を書くことを推奨します。
WHERE 句・HAVING 句での CASE 式
CASE 式は WHERE 句でも使えます。フラグ列の値によって絞り込み条件を切り替えるときに便利です。
-- 区分コードによって条件を切り替える
SELECT employee_id, last_name, salary
FROM employees
WHERE salary >
CASE department_id
WHEN 10 THEN 400000 -- 総務部は 40 万以上
WHEN 50 THEN 250000 -- 出荷部は 25 万以上
ELSE 300000 -- その他は 30 万以上
END;
-- HAVING 句でも同様に使える
SELECT department_id,
AVG(salary) AS avg_salary
FROM employees
GROUP BY department_id
HAVING AVG(salary) >
CASE department_id
WHEN 50 THEN 200000
ELSE 350000
END;
ORDER BY での CASE 式:動的な並び替え制御
CASE 式を ORDER BY に使うと、条件に応じてソート順を動的に変えることができます。特定の値を先頭や末尾に固定するパターンでよく使われます。
-- 特定のステータスを先頭に表示する(通常の ORDER BY では制御できない)
SELECT order_id, status, created_at
FROM orders
ORDER BY
-- status = 'URGENT' を最優先(0)、その他は 1 で並列に扱い日付降順
CASE WHEN status = 'URGENT' THEN 0
WHEN status = 'PENDING' THEN 1
WHEN status = 'DONE' THEN 2
ELSE 3
END,
created_at DESC;
-- NULL を末尾に固定する(NULLS LAST の代替)
SELECT employee_id, manager_id
FROM employees
ORDER BY
CASE WHEN manager_id IS NULL THEN 1 ELSE 0 END, -- NULL を後回し
manager_id ASC;
SUM(CASE WHEN …):条件集計(クロス集計の定番パターン)
条件集計(Conditional Aggregation)は、GROUP BY と CASE を組み合わせて「縦持ちデータを横に集計する」手法です。SUM(CASE WHEN 条件 THEN 1 ELSE 0 END) で条件を満たす件数を集計できます。
-- 各部門の給与帯別人数をクロス集計で一覧表示
SELECT
department_id,
COUNT(*) AS total,
SUM(CASE WHEN salary >= 800000 THEN 1 ELSE 0 END) AS grade_s,
SUM(CASE WHEN salary >= 500000
AND salary < 800000 THEN 1 ELSE 0 END) AS grade_a,
SUM(CASE WHEN salary >= 300000
AND salary < 500000 THEN 1 ELSE 0 END) AS grade_b,
SUM(CASE WHEN salary < 300000 THEN 1 ELSE 0 END) AS grade_c
FROM employees
GROUP BY department_id
ORDER BY department_id;
-- 結果:
-- DEPT_ID TOTAL GRADE_S GRADE_A GRADE_B GRADE_C
-- 10 3 1 1 1 0
-- 50 45 2 10 25 8
-- 月ごとの売上を一行に横並びで集計(PIVOT の代替として広く使われる)
SELECT
product_id,
SUM(CASE WHEN TO_CHAR(order_date,'MM') = '01' THEN amount ELSE 0 END) AS jan,
SUM(CASE WHEN TO_CHAR(order_date,'MM') = '02' THEN amount ELSE 0 END) AS feb,
SUM(CASE WHEN TO_CHAR(order_date,'MM') = '03' THEN amount ELSE 0 END) AS mar,
SUM(CASE WHEN TO_CHAR(order_date,'MM') = '04' THEN amount ELSE 0 END) AS apr,
-- ...
SUM(amount) AS total_year
FROM order_details
WHERE TO_CHAR(order_date,'YYYY') = '2025'
GROUP BY product_id
ORDER BY product_id;
-- SUM(CASE WHEN...) の代わりに COUNT + CASE で NULL を活用する書き方
-- COUNT は NULL をスキップするため、条件を満たさない行は NULL を返して除外できる
SELECT
department_id,
COUNT(*) AS total,
COUNT(CASE WHEN salary >= 800000 THEN 1 END) AS grade_s,
COUNT(CASE WHEN salary >= 500000 AND salary < 800000 THEN 1 END) AS grade_a,
COUNT(CASE WHEN salary < 300000 THEN 1 END) AS grade_c,
-- 平均も条件付きで計算できる
ROUND(AVG(CASE WHEN department_id = 50 THEN salary END)) AS dept50_avg
FROM employees
GROUP BY department_id;
-- SUM(CASE WHEN .. THEN 1 ELSE 0 END) vs COUNT(CASE WHEN .. THEN 1 END)
-- どちらも同じ結果。COUNT 版の方が ELSE が不要でシンプル
CASE を使った行→列変換(PIVOT が使えない場合の代替)
Oracle PIVOT演算子(Oracle 11g+)で同じことができますが、IN () の値を動的に変えたい場合や古い環境では CASE による条件集計が代替になります。
-- 縦持ちデータ: (region, quarter, sales)
-- → 横持ちに変換: region ごとに Q1〜Q4 の列を作る
SELECT
region,
SUM(CASE WHEN quarter = 'Q1' THEN sales ELSE 0 END) AS q1_sales,
SUM(CASE WHEN quarter = 'Q2' THEN sales ELSE 0 END) AS q2_sales,
SUM(CASE WHEN quarter = 'Q3' THEN sales ELSE 0 END) AS q3_sales,
SUM(CASE WHEN quarter = 'Q4' THEN sales ELSE 0 END) AS q4_sales,
SUM(sales) AS annual_total
FROM regional_sales
GROUP BY region
ORDER BY region;
-- Oracle 11g+ の PIVOT 構文(同じ結果):
-- SELECT * FROM regional_sales
-- PIVOT (SUM(sales) FOR quarter IN ('Q1', 'Q2', 'Q3', 'Q4'));
GROUP BY での CASE 式と Oracle 23ai の改善
Oracle 23ai より前は、GROUP BY に CASE 式を使う場合同じ式を SELECT と GROUP BY の両方に書く必要がありました。Oracle 23ai では SELECT のエイリアスを GROUP BY で使えるため、これが不要になりました。
-- Oracle 23ai 以前: GROUP BY に CASE 式をそのまま繰り返す必要がある
SELECT
CASE
WHEN salary >= 800000 THEN 'S'
WHEN salary >= 500000 THEN 'A'
WHEN salary >= 300000 THEN 'B'
ELSE 'C'
END AS salary_grade,
COUNT(*) AS headcount,
AVG(salary) AS avg_salary
FROM employees
GROUP BY
CASE
WHEN salary >= 800000 THEN 'S' -- ← 同じ CASE 式を繰り返す
WHEN salary >= 500000 THEN 'A'
WHEN salary >= 300000 THEN 'B'
ELSE 'C'
END
ORDER BY salary_grade;
-- Oracle 23ai: エイリアス salary_grade を GROUP BY でそのまま使える
SELECT
CASE
WHEN salary >= 800000 THEN 'S'
WHEN salary >= 500000 THEN 'A'
WHEN salary >= 300000 THEN 'B'
ELSE 'C'
END AS salary_grade,
COUNT(*) AS headcount,
AVG(salary) AS avg_salary
FROM employees
GROUP BY salary_grade -- ← エイリアスをそのまま使える(シンプル)
ORDER BY salary_grade;
CASE と NULL の注意点
CASE 式は WHEN NULL を等値比較で処理できません。NULL の判定には WHEN value IS NULL(検索 CASE)を使います。
-- 単純 CASE で NULL を WHEN に使っても NULL == NULL は UNKNOWN になり絶対マッチしない
SELECT employee_id,
CASE manager_id
WHEN NULL THEN 'トップ' -- ← これは絶対に実行されない
ELSE '一般'
END AS emp_type
FROM employees;
-- → manager_id が NULL でも 'トップ' にならず '一般' になる
-- 検索 CASE では IS NULL / IS NOT NULL が使える
SELECT employee_id,
CASE
WHEN manager_id IS NULL THEN 'トップ' -- NULL を正しく判定
ELSE '一般'
END AS emp_type
FROM employees;
-- NULL を含む複合条件
SELECT employee_id, salary, commission_pct,
CASE
WHEN commission_pct IS NULL THEN salary -- コミッションなし
WHEN commission_pct = 0 THEN salary -- コミッション 0%
ELSE salary + salary * commission_pct -- コミッションあり
END AS total_income
FROM employees;
PL/SQL の CASE 文(SQL の CASE 式との違い)
PL/SQL では CASE 式(値を返す)と CASE 文(処理を分岐する)の2種類があります。CASE 文は IF-ELSIF-ELSE-END IF の代替として使えます。
DECLARE
v_grade CHAR(1) := 'A';
v_msg VARCHAR2(100);
BEGIN
-- PL/SQL CASE 文(単純)
CASE v_grade
WHEN 'S' THEN v_msg := '特別評価です';
WHEN 'A' THEN v_msg := '優秀です';
WHEN 'B' THEN v_msg := '良好です';
WHEN 'C' THEN v_msg := '要改善です';
ELSE v_msg := '評価区分外です';
END CASE;
DBMS_OUTPUT.PUT_LINE(v_msg);
-- PL/SQL CASE 文(検索型): 複雑な条件で分岐
DECLARE
v_salary NUMBER := 650000;
BEGIN
CASE
WHEN v_salary >= 800000 THEN DBMS_OUTPUT.PUT_LINE('グレードS');
WHEN v_salary >= 500000 THEN DBMS_OUTPUT.PUT_LINE('グレードA');
WHEN v_salary >= 300000 THEN DBMS_OUTPUT.PUT_LINE('グレードB');
ELSE DBMS_OUTPUT.PUT_LINE('グレードC');
END CASE;
END;
END;
/
DECLARE
v_dept_id NUMBER := 50;
v_msg VARCHAR2(100);
BEGIN
-- CASE 式: := の右辺に使って値を代入
v_msg := CASE v_dept_id
WHEN 10 THEN '総務部への配属です'
WHEN 50 THEN '出荷部への配属です'
ELSE 'その他部門への配属です'
END;
DBMS_OUTPUT.PUT_LINE(v_msg);
-- DBMS_OUTPUT.PUT_LINE の引数に直接 CASE 式を埋め込む
DBMS_OUTPUT.PUT_LINE(
'部門: ' ||
CASE v_dept_id
WHEN 10 THEN '総務部'
WHEN 50 THEN '出荷部'
ELSE '不明'
END
);
END;
/
| 観点 | CASE 文(Statement) | IF-ELSIF(Statement) |
|---|---|---|
| 値の返却 | 不可(処理の分岐のみ) | 不可 |
| 可読性 | 値の等値比較なら CASE の方が簡潔 | 複雑な条件では読みやすい |
| WHEN の数が多い場合 | CASE の方が縦に整理しやすい | ELSIF が多くなり冗長 |
| NULL 判定 | 検索 CASE で WHEN col IS NULL | IF col IS NULL THEN |
CASE 式とパフォーマンスの注意点
-- WHERE 句で列を CASE 式で変換すると、その列のインデックスが使われない
SELECT * FROM employees
WHERE CASE WHEN salary > 500000 THEN 'high' ELSE 'low' END = 'high';
-- → salary 列のインデックスが使用不可
-- OK: 条件をそのまま書く
SELECT * FROM employees WHERE salary > 500000;
-- 関数ベースインデックスを作れば CASE 式でもインデックス利用可能
CREATE INDEX idx_salary_grade
ON employees (
CASE WHEN salary >= 800000 THEN 'S'
WHEN salary >= 500000 THEN 'A'
WHEN salary >= 300000 THEN 'B'
ELSE 'C'
END
);
-- 作成後: WHERE CASE WHEN salary >= 800000... END = 'S' でインデックスが使われる
まとめ
Oracle の CASE 式は SQL のあらゆる場所で使える強力な条件分岐の仕組みです。
- 単純 CASE:1列の等値比較に適す。コード変換・マスター展開に活用
- 検索 CASE:複雑な条件・範囲・NULL 判定(
IS NULL)に使う - 条件集計(
SUM(CASE WHEN...)):クロス集計・月別集計の定番パターン - ORDER BY + CASE:特定値の先頭固定・動的ソートに強力
- Oracle 23ai:GROUP BY でエイリアスが使えるようになり、CASE 式の繰り返しが不要に
CASE と DECODE の比較についてはCASE文とDECODE関数の違いと使い分け、PIVOT 演算子についてはPIVOTで縦持ちデータを横持ちに変換する方法も参照してください。

