条件式
条件式
レストランで注文する際、ウェイターが「辛いものと辛くないもの、どちらがよろしいですか?甘辛、中辛、激辛のどれにしますか?」と尋ねる場面を想像してください — これが条件ロジックです:異なる状況に基づいて異なる答えを返します。SQLのCASE WHENも同じように動作します:異なる条件に基づいて異なる値を返すことで、クエリ結果をより柔軟で意味のあるものにします。
1. コアコンセプト
シンプルCASE構文
シンプルCASEは1つの式を複数の値と比較し、プログラミング言語のswitch文に似ています。
CASE expression
WHEN value1 THEN result1
WHEN value2 THEN result2
...
ELSE default_result
END
-- 注文ステータスに基づいて説明を返す
SELECT order_id, status,
CASE status
WHEN 'completed' THEN '完了'
WHEN 'shipped' THEN '発送済み'
WHEN 'pending' THEN '保留中'
WHEN 'cancelled' THEN 'キャンセル済み'
ELSE '不明なステータス'
END AS status_description
FROM orders;
サーチCASE構文
サーチCASEは条件式を使用し、複雑なロジックに対してより柔軟性を提供します。
CASE
WHEN condition1 THEN result1
WHEN condition2 THEN result2
...
ELSE default_result
END
-- 給与レベルを分類
SELECT first_name, last_name, salary,
CASE
WHEN salary >= 15000 THEN '高'
WHEN salary >= 10000 THEN '中'
WHEN salary >= 7000 THEN '標準'
ELSE '初級'
END AS salary_level
FROM employees;
>、<、LIKE、IS NULLなど任意の条件を使用できます。ほとんどのシナリオではサーチCASEを推奨します。
SELECTでのCASE
SELECTのカラムリストでCASEを使用すると、各行に対して条件評価を行い、新しい計算カラムを生成できます。
SELECT
CONCAT(first_name, last_name) AS name,
salary,
CASE
WHEN salary >= 12000 THEN 'シニア'
WHEN salary >= 8000 THEN 'ミドル'
ELSE 'ジュニア'
END AS rank
FROM employees;
ORDER BYでのCASE
CASEを使用して「指定した順序で並べ替え」のためのカスタムソートルールを定義します。
-- ステータスによるカスタムソート:保留中 > 発送済み > 完了 > キャンセル済み
SELECT order_id, status, total_amount
FROM orders
ORDER BY
CASE status
WHEN 'pending' THEN 1
WHEN 'shipped' THEN 2
WHEN 'completed' THEN 3
WHEN 'cancelled' THEN 4
END;
GROUP BYでのCASE
集約関数と組み合わせることで、条件付き統計(行から列へのピボット)が可能になり、データ分析の一般的なテクニックです。
-- ステータス別に注文数を集計
SELECT
SUM(CASE WHEN status = 'completed' THEN 1 ELSE 0 END) AS completed,
SUM(CASE WHEN status = 'shipped' THEN 1 ELSE 0 END) AS shipped,
SUM(CASE WHEN status = 'pending' THEN 1 ELSE 0 END) AS pending,
SUM(CASE WHEN status = 'cancelled' THEN 1 ELSE 0 END) AS cancelled
FROM orders;
UPDATEでのCASE
UPDATE文でCASEを使用すると、バッチ条件付き更新が可能になり、単一のSQL文で複数の更新ロジックを完了できます。
-- パフォーマンスに基づいて給与を調整
UPDATE employees
SET salary = CASE
WHEN salary < 8000 THEN salary * 1.15
WHEN salary < 12000 THEN salary * 1.10
ELSE salary * 1.05
END;
IF / IFNULL / COALESCE
MySQLはCASEよりも簡潔な条件関数を提供しています:
| 関数 | 構文 | 用途 |
|---|---|---|
IF |
IF(condition, true_value, false_value) |
簡単な二択 |
IFNULL |
IFNULL(expression, replacement) |
NULLの場合に代替値を返す |
COALESCE |
COALESCE(value1, value2, ...) |
最初のNULLでない値を返す |
-- IF:簡単な二択
SELECT IF(salary > 10000, '高', '通常') AS level FROM employees;
-- IFNULL:NULLの処理
SELECT IFNULL(department_id, 0) AS department_id FROM employees;
-- COALESCE:最初のNULLでない値を返す
SELECT COALESCE(commission, bonus, 0) AS bonus FROM employees;
COALESCEはSQL標準関数で複数の引数をサポートし、IFNULLはMySQL固有で2つの引数しかサポートしません。互換性のためにCOALESCEを推奨します。
2. 基本構文
CASE WHENの完全な構文
-- シンプルCASE
CASE expression
WHEN value1 THEN result1
WHEN value2 THEN result2
[ELSE default_result]
END
-- サーチCASE
CASE
WHEN condition1 THEN result1
WHEN condition2 THEN result2
[ELSE default_result]
END
条件関数の構文
IF(condition, true_value, false_value)
IFNULL(expression, replacement)
COALESCE(value1, value2, ..., valueN)
COALESCEはIFNULLを置き換えでき、複数のフォールバック値をサポートするため推奨します。
例:注文にステータスラベルと優先度を追加(難易度⭐)
SELECT
order_id,
CONCAT(e.first_name, e.last_name) AS owner,
o.total_amount AS amount,
CASE status
WHEN 'completed' THEN '✅ 完了'
WHEN 'shipped' THEN '🚚 発送済み'
WHEN 'pending' THEN '⏳ 保留中'
WHEN 'cancelled' THEN '❌ キャンセル済み'
ELSE '❓ 不明'
END AS status_label,
CASE
WHEN total_amount >= 3000 THEN '高'
WHEN total_amount >= 1000 THEN '中'
ELSE '低'
END AS priority
FROM orders o
LEFT JOIN employees e ON o.employee_id = e.employee_id
ORDER BY
CASE
WHEN total_amount >= 3000 THEN 1
WHEN total_amount >= 1000 THEN 2
ELSE 3
END;
出力:
order_id | owner | amount | status_label | priority
---------+----------+-----------+-----------------+---------
1005 | ZhaoMin | 4100.00 | ✅ 完了 | 高
1003 | ZhaoMin | 3200.00 | 🚚 発送済み | 高
1006 | WangQiang| 2800.00 | ❌ キャンセル済み | 中
1001 | WangQiang| 2500.00 | ✅ 完了 | 中
1002 | WangQiang| 1800.00 | ✅ 完了 | 中
1004 | ZhangWei | 950.00 | ⏳ 保留中 | 低
クエリのアプローチ:
- 最初のCASEは英語のステータスをラベル付きの説明に変換します
- 2番目のCASEは金額で優先度を分類します
- ORDER BYのCASEは優先度で並べ替えます
例:部門ごとの給与レベル別従業員数を集計(難易度⭐⭐)
SELECT
d.department_name AS department,
COUNT(*) AS total_count,
SUM(CASE WHEN e.salary >= 12000 THEN 1 ELSE 0 END) AS high_salary,
SUM(CASE WHEN e.salary >= 8000 AND e.salary < 12000 THEN 1 ELSE 0 END) AS medium_salary,
SUM(CASE WHEN e.salary < 8000 THEN 1 ELSE 0 END) AS standard_salary,
AVG(e.salary) AS avg_salary
FROM employees e
LEFT JOIN departments d ON e.department_id = d.department_id
GROUP BY d.department_name
ORDER BY avg_salary DESC;
出力:
department | total_count | high_salary | medium_salary | standard_salary | avg_salary
-----------+-------------+-------------+---------------+-----------------+-----------
Technology | 2 | 1 | 1 | 0 | 10750.00
Sales | 2 | 1 | 1 | 0 | 9500.00
Marketing | 1 | 0 | 0 | 1 | 7500.00
NULL | 1 | 0 | 1 | 0 | 9000.00
クエリのアプローチ:
- GROUP BYで部門ごとにグループ化
- 各SUM(CASE ...)は条件を満たす従業員をカウントし、行から列へのピボットを実現
- 同時に平均給与を参照値として計算
3. 一般的な応用シナリオ
シナリオ1:動的ラベル — 多次元条件に基づく従業員のタグ付け
SELECT
CONCAT(e.first_name, e.last_name) AS name,
d.department_name AS department,
e.salary,
CASE
WHEN e.salary >= 12000 AND d.department_name = 'Technology' THEN 'コア技術スタッフ'
WHEN e.salary >= 12000 THEN 'シニア従業員'
WHEN e.salary >= 8000 AND e.hire_date < '2023-06-01' THEN '経験豊富なミドル'
WHEN e.salary >= 8000 THEN 'ミドル従業員'
ELSE 'ジュニア従業員'
END AS employee_tag
FROM employees e
LEFT JOIN departments d ON e.department_id = d.department_id
ORDER BY e.salary DESC;
出力:
name | department | salary | employee_tag
----------+------------+-----------+----------------------
ZhangWei | Technology | 12000.00 | コア技術スタッフ
ZhaoMin | Sales | 11000.00 | シニア従業員
LiNa | Technology | 9500.00 | 経験豊富なミドル
ChenJing | NULL | 9000.00 | ミドル従業員
WangQiang | Sales | 8000.00 | 経験豊富なミドル
LiuYang | Marketing | 7500.00 | ジュニア従業員
シナリオ2:UPDATE + CASEによるバッチ条件付き更新
-- 勤続年数と部門に基づいて給与を調整
UPDATE employees
SET salary = CASE
WHEN department_id = 1 AND hire_date < '2023-01-01' THEN salary * 1.15
WHEN department_id = 1 THEN salary * 1.10
WHEN department_id = 2 THEN salary * 1.08
WHEN department_id IS NULL THEN salary * 1.05
ELSE salary * 1.03
END;
❓ よくある質問
質問:CASE WHENとIFの違いは何ですか?どちらを使うべきですか? 回答:
IFは二択(真/偽)しか処理できず、簡単なシナリオに適しています;CASE WHENは複数の条件分岐を処理でき、複雑なロジックに適しています。MySQLではIFがより簡潔ですが、CASE WHENはSQL標準でデータベース間の互換性がより高いです。複雑な条件にはCASEを、簡単なチェックにはIFまたはCOALESCEを使用してください。
質問:CASE式をWHERE句で使用できますか? 回答: はいできますが、推奨しません。条件は通常WHEREに直接記述すべきで、CASEで囲むべきではありません。CASEはSELECTで新しいカラムを生成し、ORDER BYでカスタムソートを行い、GROUP BYで条件付き集約を行うのに適しています。
質問:CASE WHENの実行順序はどうなっていますか? 回答: CASEは記述順にWHIN節を評価し、WHIN条件が真になると対応するTHEN値を返し、それ以降のWHIN節は評価されません。最も一般的または優先度の高い条件を最初に配置してください。
質問:COALESCEとIFNULLのどちらが優れていますか? 回答:
COALESCEはSQL標準関数で複数の引数(例:COALESCE(a, b, c, 0))をサポートし、データベース間で動作します;IFNULLはMySQL固有で2つの引数しかサポートしません。互換性と柔軟性のためにCOALESCEを推奨します。
📖 まとめ
CASE WHENはSQLの条件式で、SELECT、WHERE、ORDER BY、GROUP BYなどに出現できます- シンプルCASE:等価比較、「1つのフィールドが複数の値にマッチする」場合に適しています
- サーチCASE:任意の条件、複雑なロジックに適しており、デフォルトの選択として推奨
- GROUP BYでのCASE:SUM/COUNTと組み合わせた条件付き統計(行から列へのピボット)、データ分析のコアテクニック
- UPDATEでのCASE:単一のUPDATEで複数の更新操作を完了
- IF/IFNULL/COALESCE:より簡潔な条件関数;COALESceは互換性が最も高い
📝 演習
演習1(⭐):すべての注文をクエリし、「金額帯」カラムを追加してください:金額 >= 3000は「大口注文」、1000-2999は「中口注文」、< 1000は「小口注文」。金額帯で並べ替えてください。
演習2(⭐⭐):CASE WHENと集約関数を使用して従業員ごとの注文統計を集約し、次のレポートを生成してください:従業員名、総注文数、完了注文数、保留中注文数、総売上。
演習3(⭐⭐):UPDATE + CASEを使用して、「営業」部門の従業員の給与を10%、「技術」部門は8%、その他の部門は5%引き上げてください。
次のレッスン
👉 17-views-indexes - ビューとインデックス:ビュー(仮想テーブル)の作成と使用方法、およびインデックスの概念と最適化戦略を学びましょう!



