条件式

条件式

レストランで注文する際、ウェイターが「辛いものと辛くないもの、どちらがよろしいですか?甘辛、中辛、激辛のどれにしますか?」と尋ねる場面を想像してください — これが条件ロジックです:異なる状況に基づいて異なる答えを返します。SQLのCASE WHENも同じように動作します:異なる条件に基づいて異なる値を返すことで、クエリ結果をより柔軟で意味のあるものにします。


1. コアコンセプト

シンプルCASE構文

シンプルCASEは1つの式を複数の値と比較し、プログラミング言語のswitch文に似ています。

SQL
CASE expression
    WHEN value1 THEN result1
    WHEN value2 THEN result2
    ...
    ELSE default_result
END
SQL
-- 注文ステータスに基づいて説明を返す
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は条件式を使用し、複雑なロジックに対してより柔軟性を提供します。

SQL
CASE
    WHEN condition1 THEN result1
    WHEN condition2 THEN result2
    ...
    ELSE default_result
END
SQL
-- 給与レベルを分類
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;
💡 シンプルCASE vs サーチCASE:シンプルCASEは等価比較しかできませんが、サーチCASEは><LIKEIS NULLなど任意の条件を使用できます。ほとんどのシナリオではサーチCASEを推奨します。

SELECTでのCASE

SELECTのカラムリストでCASEを使用すると、各行に対して条件評価を行い、新しい計算カラムを生成できます。

SQL
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を使用して「指定した順序で並べ替え」のためのカスタムソートルールを定義します。

SQL
-- ステータスによるカスタムソート:保留中 > 発送済み > 完了 > キャンセル済み
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

集約関数と組み合わせることで、条件付き統計(行から列へのピボット)が可能になり、データ分析の一般的なテクニックです。

SQL
-- ステータス別に注文数を集計
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文で複数の更新ロジックを完了できます。

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でない値を返す
SQL
-- 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 vs IFNULLCOALESCEはSQL標準関数で複数の引数をサポートし、IFNULLはMySQL固有で2つの引数しかサポートしません。互換性のためにCOALESCEを推奨します。


2. 基本構文

CASE WHENの完全な構文

SQL
-- シンプル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
💡 ヒント:CASE式は値を返し、式が使用できる場所ならどこにでも出現できます(SELECT、WHERE、ORDER BY、GROUP BYなど)。WHIN節は記述順に評価され、一致が見つかると結果が返され、それ以降のWHIN節は評価されません。

💡 ヒント:ELSEを省略し、一致するものがない場合、CASEはNULLを返します。予期しないケースを処理するために、常にELSEを含めることを推奨します。

条件関数の構文

SQL
IF(condition, true_value, false_value)
IFNULL(expression, replacement)
COALESCE(value1, value2, ..., valueN)
💡 ヒントCOALESCEIFNULLを置き換えでき、複数のフォールバック値をサポートするため推奨します。


例:注文にステータスラベルと優先度を追加(難易度⭐)

SQL
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;
▶ 試してみよう

出力:

TEXT
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   | ⏳ 保留中        | 低

クエリのアプローチ:

  1. 最初のCASEは英語のステータスをラベル付きの説明に変換します
  2. 2番目のCASEは金額で優先度を分類します
  3. ORDER BYのCASEは優先度で並べ替えます

例:部門ごとの給与レベル別従業員数を集計(難易度⭐⭐)

SQL
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;
▶ 試してみよう

出力:

TEXT
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

クエリのアプローチ:

  1. GROUP BYで部門ごとにグループ化
  2. 各SUM(CASE ...)は条件を満たす従業員をカウントし、行から列へのピボットを実現
  3. 同時に平均給与を参照値として計算

3. 一般的な応用シナリオ

シナリオ1:動的ラベル — 多次元条件に基づく従業員のタグ付け

SQL
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;

出力:

TEXT
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  | ジュニア従業員
💡 アプローチ:サーチCASEのWHIN節は記述順に評価され、一致が見つかると即座に返されるため、条件の順序が重要です — より具体的な条件を最初に記述してください。

シナリオ2:UPDATE + CASEによるバッチ条件付き更新

SQL
-- 勤続年数と部門に基づいて給与を調整
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;
💡 アプローチ:単一のUPDATEで複数の更新ロジックを完了でき、複数のUPDATE文を書く必要がなくなります。CASEは記述順に評価されるため、「技術部門かつ初期採用」の条件は「技術部門」の前に記述されています。


❓ よくある質問

質問: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を推奨します。


📖 まとめ


📝 演習

演習1(⭐):すべての注文をクエリし、「金額帯」カラムを追加してください:金額 >= 3000は「大口注文」、1000-2999は「中口注文」、< 1000は「小口注文」。金額帯で並べ替えてください。

演習2(⭐⭐):CASE WHENと集約関数を使用して従業員ごとの注文統計を集約し、次のレポートを生成してください:従業員名、総注文数、完了注文数、保留中注文数、総売上。

演習3(⭐⭐):UPDATE + CASEを使用して、「営業」部門の従業員の給与を10%、「技術」部門は8%、その他の部門は5%引き上げてください。


次のレッスン

👉 17-views-indexes - ビューとインデックス:ビュー(仮想テーブル)の作成と使用方法、およびインデックスの概念と最適化戦略を学びましょう!

Web-Tutorial.com

Web-Tutorial 技術チーム

複数の開発者によって共同維持されているプログラミングチュートリアルプラットフォーム。各チュートリアルは専門分野の開発者が執筆・レビューしています。正確で信頼性の高いコンテンツを目指しています — 問題を見つけた場合はお知らせください。

100%