Oracle で条件分岐を行うには CASE 式と DECODE 関数の 2 つの方法があります。どちらも「条件に応じて異なる値を返す」機能ですが、NULL の扱い・型変換の動作・SQL 標準への準拠・PL/SQL での使い方が異なります。
本記事では、CASE 式の 2 つの構文(単純 CASE / 検索 CASE)と DECODE を比較しながら、NULL の等値判定の違い、暗黙型変換の罠、パフォーマンスの差異、PL/SQL での CASE 文、そしてORDER BY・GROUP BY・UPDATE 内での CASE 活用まで解説します。
・単純 CASE 式と検索 CASE 式の構文と使い分け
・DECODE 関数の構文(概要)
・CASE vs DECODE の完全比較表
・NULL の扱いの違い(DECODE: NULL=NULL、CASE: IS NULL が必要)
・戻り値の型変換の違い(DECODE: 暗黙変換、CASE: 明示的)
・パフォーマンスの違い
・PL/SQL での CASE 式と CASE 文
・ORDER BY / GROUP BY / UPDATE / INSERT での CASE 活用パターン
CASE 式の構文
単純 CASE 式(等値比較)
-- 構文: CASE 式 WHEN 値 THEN 結果 ... ELSE デフォルト END
SELECT employee_id,
department_id,
CASE department_id
WHEN 10 THEN '経理部'
WHEN 20 THEN '研究開発部'
WHEN 30 THEN '営業部'
ELSE 'その他'
END AS dept_name
FROM employees;
検索 CASE 式(任意の条件)
-- 構文: CASE WHEN 条件 THEN 結果 ... ELSE デフォルト END
SELECT employee_id,
salary,
CASE
WHEN salary >= 10000 THEN 'A(高給)'
WHEN salary >= 5000 THEN 'B(中間)'
WHEN salary >= 3000 THEN 'C(標準)'
ELSE 'D(見直し対象)'
END AS salary_grade
FROM employees;
| 種類 | 比較方式 | 適するケース |
|---|---|---|
| 単純 CASE | 等値(=)のみ | 1 つの列の値でマッピング(コード→名称変換など) |
| 検索 CASE | 任意の条件式(>=, BETWEEN, LIKE, IS NULL 等) | 範囲判定・複合条件・NULL 判定が必要な場合 |
単純 CASE は等値比較しかできず、後から「範囲判定も追加したい」となると書き換えが必要です。検索 CASE なら等値比較も範囲判定もすべて対応できるため、汎用性が高いです。
DECODE 関数の構文(概要)
-- 構文: DECODE(式, 比較値1, 結果1, 比較値2, 結果2, ..., デフォルト)
SELECT employee_id,
department_id,
DECODE(department_id,
10, '経理部',
20, '研究開発部',
30, '営業部',
'その他'
) AS dept_name
FROM employees;
DECODE は等値比較のみに対応する Oracle 独自の関数です。上記の例は単純 CASE とまったく同じ結果を返します。DECODE の詳しい使い方は「DECODE 関数の使い方完全解説」を参照してください。
CASE vs DECODE 完全比較表
| 項目 | CASE 式 | DECODE 関数 |
|---|---|---|
| SQL 標準 | SQL 標準(全 RDBMS 対応) | Oracle 独自 |
| 比較方式 | 等値(単純CASE)+ 任意条件(検索CASE) | 等値のみ |
| NULL の比較 | IS NULL が必要 (WHEN NULL は動作しない) |
NULL = NULL として扱う (特別な記述不要) |
| 戻り値の型 | 最も広い型に統一(明示的なルール) | 第 1 結果の型に暗黙変換 |
| ネスト | 自然に読める | 可読性が大幅に低下 |
| 可読性 | 高い(条件が明示的) | 低い(引数の羅列) |
| PL/SQL | CASE 式 + CASE 文の両方使える | SQL 内でのみ(PL/SQL 文としては不可) |
| パフォーマンス | 同等 | 同等(オプティマイザが内部変換) |
| 使える場所 | SELECT / WHERE / ORDER BY / GROUP BY / UPDATE / INSERT / PL/SQL | SELECT / WHERE / ORDER BY / GROUP BY / UPDATE / INSERT |
NULL の扱いの違い
CASE と DECODE で最も注意すべき違いが NULL の比較動作です。
-- DECODE: NULL = NULL として扱う(特別な記述不要) SELECT DECODE(NULL, NULL, 'MATCH', 'NO MATCH') FROM DUAL; -- 結果: MATCH(NULL 同士を等しいと判定) -- 単純 CASE: WHEN NULL は動作しない SELECT CASE NULL WHEN NULL THEN 'MATCH' ELSE 'NO MATCH' END FROM DUAL; -- 結果: NO MATCH(NULL = NULL は UNKNOWN → ELSE に流れる) -- 検索 CASE: IS NULL を使えば判定できる SELECT CASE WHEN NULL IS NULL THEN 'MATCH' ELSE 'NO MATCH' END FROM DUAL; -- 結果: MATCH
-- 列に NULL がある場合のマッピング
-- DECODE: commission_pct が NULL なら '固定給'
SELECT DECODE(commission_pct, NULL, '固定給', '歩合あり') FROM employees;
-- CASE: IS NULL を使う
SELECT CASE WHEN commission_pct IS NULL THEN '固定給'
ELSE '歩合あり'
END FROM employees;
-- 単純 CASE では NULL 判定できない(検索 CASE を使う)
・NULL を頻繁に比較する場面 →
DECODE がシンプル(NULL = NULL が自動判定)・それ以外の場面 →
CASE WHEN col IS NULL が明確で安全ただし NVL / NVL2 で NULL を事前に変換する方がさらにシンプルなことが多いです。
戻り値の型変換の違い
-- DECODE: 第 1 結果の型に合わせて暗黙変換 SELECT DECODE(1, 1, 'TEXT', 2, 100) FROM DUAL; -- 第 1 結果 'TEXT' が VARCHAR2 → 100 も VARCHAR2 に暗黙変換 → '100' -- CASE: 全分岐の型から最も広い型を自動推定 SELECT CASE 1 WHEN 1 THEN 'TEXT' WHEN 2 THEN 100 END FROM DUAL; -- ORA-00932: inconsistent datatypes(VARCHAR2 と NUMBER は混在不可)
DECODE は第 1 結果の型に後続の結果を暗黙変換するため、意図しない型変換(例: 数値が文字列になる)が発生します。CASE 式は型の不一致をエラーにしてくれるため、バグを早期発見できます。安全性の面では CASE 式が優位です。
DECODE から CASE への書き換え
| DECODE | CASE 書き換え |
|---|---|
| DECODE(col, ‘A’, 1, ‘B’, 2, 0) | CASE col WHEN ‘A’ THEN 1 WHEN ‘B’ THEN 2 ELSE 0 END |
| DECODE(col, NULL, ‘NULL!’, col) | CASE WHEN col IS NULL THEN ‘NULL!’ ELSE col END |
| DECODE(SIGN(a-b), 1, ‘a>b’, -1, ‘a<b’, ‘a=b’) | CASE WHEN a > b THEN ‘a>b’ WHEN a < b THEN ‘a<b’ ELSE ‘a=b’ END |
| DECODE(status, 1, DECODE(type, A, X, Y), Z) | CASE WHEN status=1 AND type=’A’ THEN X WHEN status=1 THEN Y ELSE Z END |
DECODE のネスト(DECODE の中に DECODE)は可読性が著しく低下します。検索 CASE に書き換えれば、条件が一目瞭然になります。新規開発では CASE を使い、既存コードの DECODE は機会があれば CASE に移行することを推奨します。
PL/SQL での CASE
PL/SQL では CASE を式(値を返す)と文(処理を実行する)の2 通りで使えます。DECODE は SQL 文の中でしか使えません。
DECLARE
v_dept_id NUMBER := 10;
v_name VARCHAR2(50);
BEGIN
-- CASE 式: 変数に代入
v_name := CASE v_dept_id
WHEN 10 THEN '経理部'
WHEN 20 THEN '研究開発部'
ELSE 'その他'
END;
DBMS_OUTPUT.PUT_LINE(v_name);
END;
/
DECLARE
v_grade CHAR(1) := 'A';
BEGIN
-- CASE 文: 分岐ごとに処理を実行
CASE v_grade
WHEN 'A' THEN
DBMS_OUTPUT.PUT_LINE('Excellent');
WHEN 'B' THEN
DBMS_OUTPUT.PUT_LINE('Good');
WHEN 'C' THEN
DBMS_OUTPUT.PUT_LINE('Average');
ELSE
DBMS_OUTPUT.PUT_LINE('Below Average');
END CASE; -- END CASE(セミコロンあり)
END;
/
| 種類 | 末尾の書き方 | 用途 |
|---|---|---|
| CASE 式(SQL / PL/SQL 両方) | END(セミコロンなし) |
値を返す(SELECT / 代入 / 引数に使用) |
| CASE 文(PL/SQL のみ) | END CASE;(セミコロンあり) |
処理を分岐実行(IF-ELSIF の代替) |
実務パターン集
パターン(1): ORDER BY での並び替え制御
-- ステータス: ACTIVE → PENDING → INACTIVE の順に表示
SELECT employee_id, last_name, status
FROM employees
ORDER BY
CASE status
WHEN 'ACTIVE' THEN 1
WHEN 'PENDING' THEN 2
WHEN 'INACTIVE' THEN 3
ELSE 4
END;
パターン(2): GROUP BY + CASE で条件付き集計
-- 給与を 3 段階に分けて人数を集計
SELECT
CASE
WHEN salary >= 10000 THEN '高給(10000以上)'
WHEN salary >= 5000 THEN '中間(5000-9999)'
ELSE '標準(5000未満)'
END AS salary_band,
COUNT(*) AS emp_count,
AVG(salary) AS avg_salary
FROM employees
GROUP BY
CASE
WHEN salary >= 10000 THEN '高給(10000以上)'
WHEN salary >= 5000 THEN '中間(5000-9999)'
ELSE '標準(5000未満)'
END
ORDER BY avg_salary DESC;
パターン(3): UPDATE SET に CASE を使う
-- 部門ごとに異なる昇給率を適用
UPDATE employees
SET salary = salary * CASE department_id
WHEN 10 THEN 1.10 -- 経理: 10%
WHEN 20 THEN 1.15 -- 研究開発: 15%
WHEN 30 THEN 1.05 -- 営業: 5%
ELSE 1.08 -- その他: 8%
END
WHERE status = 'ACTIVE';
パターン(4): SELECT で横展開(ピボット的な使い方)
-- 月別の注文件数を横展開(PIVOT の代替)
SELECT
product_name,
SUM(CASE WHEN TO_CHAR(order_date, 'MM') = '01' THEN 1 ELSE 0 END) AS jan,
SUM(CASE WHEN TO_CHAR(order_date, 'MM') = '02' THEN 1 ELSE 0 END) AS feb,
SUM(CASE WHEN TO_CHAR(order_date, 'MM') = '03' THEN 1 ELSE 0 END) AS mar
FROM orders
WHERE order_date >= DATE '2026-01-01'
GROUP BY product_name;
パターン(5): WHERE 句での CASE
-- パラメータに応じて検索条件を切り替え
SELECT * FROM employees
WHERE department_id = CASE :search_mode
WHEN 'ALL' THEN department_id -- 全部門
ELSE :target_dept_id -- 指定部門
END;
使い分けの判断基準
| 場面 | 推奨 | 理由 |
|---|---|---|
| 新規開発 | CASE | SQL 標準。可読性が高い。型安全 |
| 範囲判定(>=, BETWEEN 等) | 検索 CASE | DECODE は等値比較しかできない |
| NULL の等値比較が多い | DECODE がシンプル | DECODE は NULL = NULL を自動判定 |
| 他 RDBMS への移植が必要 | CASE | DECODE は Oracle 独自 |
| PL/SQL 内の分岐処理 | CASE 文 | DECODE は PL/SQL 文として使えない |
| 既存コードが DECODE | そのまま維持 / 余裕があれば CASE に移行 | 動作に問題がなければ無理に変えない |
CASE は SQL 標準で全 RDBMS 対応、等値も範囲も対応、型安全、PL/SQL でも使える――とあらゆる面で DECODE を上回ります。DECODE を積極的に選ぶ理由は「NULL の等値比較が多い場面」と「既存コードの保守」くらいです。新規開発では CASE を標準として使ってください。
よくある質問
CASE col WHEN NULL THEN ... は動作しません。内部的に col = NULL として評価され、常に UNKNOWN になるためです。NULL 判定は検索 CASE(CASE WHEN col IS NULL THEN ...)を使ってください。END。CASE 文は PL/SQL 専用で、処理を分岐実行します。末尾は
END CASE;。CASE 式は SQL でも PL/SQL でも使えますが、CASE 文は PL/SQL のみです。
まとめ
CASE 式と DECODE 関数の使い分けの要点をまとめます。
| やりたいこと | 推奨方法 |
|---|---|
| 等値比較で値をマッピング | CASE col WHEN ‘A’ THEN 1 … END(または DECODE) |
| 範囲判定(>= / BETWEEN / LIKE) | CASE WHEN salary >= 5000 THEN … END |
| NULL の列を別の値に変換 | CASE WHEN col IS NULL THEN … END(または DECODE(col, NULL, …)) |
| ORDER BY の並び順をカスタマイズ | ORDER BY CASE status WHEN … END |
| GROUP BY で条件付き集計 | SUM(CASE WHEN cond THEN 1 ELSE 0 END) |
| UPDATE で条件付き更新 | SET col = CASE WHEN … END |
| PL/SQL で分岐処理 | CASE … WHEN … THEN … END CASE; |
| 他 RDBMS への移植が必要 | CASE(SQL 標準) |
DECODE 関数の詳しい使い方は「DECODE 関数の使い方完全解説」も併せて参照してください。
CASE式の条件集計(SUM/COUNT CASE WHEN)・ORDER BY動的ソート・PIVOT代替・Oracle 23aiでのGROUP BYエイリアス改善など、SQL全般での活用パターンはCASE式完全ガイドも参照してください。

