この記事では、SQLのCASE式を使った条件分岐の方法を習得できます。単純CASE式と検索CASE式の2つの基本構文から、WHERE句、ORDER BY句、UPDATE文での応用まで、実践的なサンプルコードとともに解説。複数条件の指定、入れ子構造、集計での活用など、様々な使い方を学べます。IF文との違いや注意点も理解でき、データ取得や更新処理を自在に操る技術が身につきます。
目次
SQLのCASE式とは?基本概要と使いどころ
SQL CASE式は、データベース内のデータに対して条件に応じた値の変換や分岐処理を行うための強力な機能です。プログラミング言語におけるif文やswitch文に相当する役割を果たし、SELECT文やUPDATE文など様々なSQL文の中で利用できます。
CASE式を使用することで、取得したデータをそのまま表示するのではなく、特定の条件に基づいて異なる値を返すことが可能になります。例えば、数値データを「高」「中」「低」といった分類ラベルに変換したり、NULL値を適切な代替値に置き換えたりといった処理を、SQL文の中で直接記述できます。
CASE式が特に効果を発揮する場面は以下の通りです:
- データの分類と表示形式の変換 – 売上金額を「A」「B」「C」ランクに分類するなど、数値や文字列を人間が理解しやすい形式に変換
- 集計処理での条件付きカウント – 特定条件を満たすレコードのみを集計対象とする柔軟な集計処理
- NULL値やエラーデータの処理 – データの欠損や異常値に対して適切なデフォルト値を設定
- 複雑なソート順の実現 – 通常のORDER BYでは表現できない独自の並び順を定義
- 条件付きデータ更新 – UPDATE文で異なる条件に応じて異なる更新値を一度に設定
CASE式の大きな利点は、データベース側で条件分岐処理を完結できることです。アプリケーション側でデータを取得してから条件判定を行う必要がないため、パフォーマンスの向上やコードの簡潔化につながります。また、複数のテーブル結合や集計処理と組み合わせることで、一つのSQL文で複雑なビジネスロジックを表現できるようになります。
データ分析やレポート作成の場面では、CASE式は必須のテクニックと言えます。生のデータをそのまま出力するのではなく、ビジネス要件に応じた形式に加工して出力することで、より実用的で理解しやすい結果を得ることができます。
SQLを扱う上で、CASE式は基本的なSELECT文やJOIN句と同様に、習得すべき重要な構文の一つです。最初は複雑に感じるかもしれませんが、基本構文を理解すれば、データベース操作の表現力が大きく広がります。
“`html
CASE式の基本構文と書き方
SQLのCASE式には、「単純CASE式」と「検索CASE式」の2つの記述方法があります。どちらも条件に応じた値を返す機能は同じですが、構文の書き方や適用できる条件の複雑さが異なります。ここでは、それぞれの構文の違いと使い分けのポイントを詳しく解説していきます。適切な構文を選択することで、より読みやすく保守性の高いSQLコードを書くことができるようになります。
単純CASE式の構文
単純CASE式は、特定のカラムや式の値を直接比較する際に使用する構文です。構文がシンプルで可読性が高いため、等値比較を行う場合に適しています。
基本的な構文は以下の通りです。
CASE 評価する式
WHEN 値1 THEN 結果1
WHEN 値2 THEN 結果2
WHEN 値3 THEN 結果3
ELSE デフォルト結果
END
具体的な使用例を見てみましょう。商品の区分コードに応じてカテゴリ名を表示する場合は次のように記述します。
SELECT
product_name,
CASE category_code
WHEN 1 THEN '食品'
WHEN 2 THEN '衣料品'
WHEN 3 THEN '家電'
ELSE 'その他'
END AS category_name
FROM products;
この構文では、category_code
というカラムの値が1、2、3のいずれかに一致するかを順番に評価し、一致した場合は対応する結果を返します。どの条件にも一致しない場合は、ELSE
句で指定したデフォルト値が返されます。なお、ELSE
句は省略可能ですが、省略した場合はNULL
が返されるため、明示的に指定することが推奨されます。
検索CASE式の構文
検索CASE式は、より複雑な条件式を評価できる構文で、単純CASE式では対応できない不等号比較や複数条件の組み合わせなどに対応しています。
基本的な構文は以下の通りです。
CASE
WHEN 条件式1 THEN 結果1
WHEN 条件式2 THEN 結果2
WHEN 条件式3 THEN 結果3
ELSE デフォルト結果
END
単純CASE式との違いは、CASE
キーワードの直後に評価する式を記述せず、各WHEN
句で独立した条件式を記述する点です。これにより、様々な比較演算子や論理演算子を使った柔軟な条件判定が可能になります。
具体的な使用例として、売上金額に応じてランクを付ける場合を見てみましょう。
SELECT
customer_name,
sales_amount,
CASE
WHEN sales_amount >= 1000000 THEN 'プラチナ'
WHEN sales_amount >= 500000 THEN 'ゴールド'
WHEN sales_amount >= 100000 THEN 'シルバー'
ELSE 'ブロンズ'
END AS customer_rank
FROM sales;
この例では、sales_amount
の値を不等号で比較し、条件に応じた顧客ランクを返しています。条件は上から順に評価され、最初に真となった条件の結果が返されるため、条件の記述順序が重要になります。
単純CASE式と検索CASE式の使い分け
2つのCASE式は状況に応じて使い分けることで、SQLコードの可読性と保守性を高めることができます。それぞれの特徴を理解し、適切な構文を選択しましょう。
単純CASE式を使うべき場面は以下の通りです。
- 特定のカラムやフィールドの値を等値比較する場合
- 比較する値が明確に決まっている場合
- コードをシンプルで読みやすく保ちたい場合
- ステータスコードや区分値など、固定値との比較を行う場合
検索CASE式を使うべき場面は以下の通りです。
- 範囲指定や不等号を使った比較が必要な場合
- 複数のカラムを組み合わせた条件判定を行う場合
LIKE
やIN
、BETWEEN
などの演算子を使用する場合- NULL判定や論理演算子(AND、OR)を使った複雑な条件を設定する場合
- 各条件で異なるカラムや式を評価したい場合
実際の開発では、まず単純CASE式で記述できないかを検討し、複雑な条件が必要な場合にのみ検索CASE式を使用するという判断基準が有効です。以下の比較表を参考にしてください。
比較項目 | 単純CASE式 | 検索CASE式 |
---|---|---|
構文の簡潔さ | シンプルで読みやすい | やや複雑 |
比較方法 | 等値比較のみ | あらゆる条件式が可能 |
使用できる演算子 | 暗黙的な「=」のみ | すべての比較演算子 |
複数条件の組み合わせ | 不可 | 可能(AND、OR使用) |
パフォーマンス | わずかに高速 | 条件次第 |
なお、同じ結果を得られる場合でも、チーム内のコーディング規約やプロジェクトの方針に従って統一的な記述方法を選択することが、保守性の観点から重要です。
“`
CASE式の戻り値と評価ルール
CASE式を適切に活用するためには、戻り値の型や評価順序などのルールを正しく理解しておくことが重要です。このセクションでは、CASE式がどのように評価され、どのような値を返すのかについて詳しく解説します。
CASE式の評価順序
CASE式は上から順に条件を評価し、最初に真となった条件のRESULT値を返すという動作をします。いったん条件に合致すると、その時点で評価を終了し、以降の条件は評価されません。この仕組みは、プログラミング言語におけるif-else if文と同様の振る舞いです。
CASE
WHEN age 20 THEN '未成年'
WHEN age 65 THEN '成人'
WHEN age >= 65 THEN '高齢者'
END
上記の例では、ageが30の場合、最初の条件(age 20)は偽となりますが、2番目の条件(age 65)が真となるため「成人」が返されます。3番目の条件は評価されることはありません。
ELSE句が指定されていない場合の挙動
CASE式でELSE句を省略した場合、すべての条件に合致しないときはNULLが返されます。この動作は意図しない結果を招く可能性があるため、注意が必要です。
SELECT
product_name,
CASE
WHEN price 1000 THEN '低価格'
WHEN price 5000 THEN '中価格'
END AS price_range
FROM products;
この例では、priceが5000以上の商品に対してはprice_range列にNULLが設定されます。予期しないNULL値を避けるためには、ELSE句を明示的に指定することを推奨します。
戻り値のデータ型
CASE式から返される値のデータ型は、THEN句とELSE句で指定されたすべての値の中で最も優先度の高いデータ型に統一されます。データベースシステムは自動的に型の変換を試みますが、互換性のない型が混在している場合はエラーが発生します。
組み合わせ | 結果のデータ型 |
---|---|
整数型と整数型 | 整数型 |
整数型と小数型 | 小数型 |
文字列型と文字列型 | 文字列型(長い方に合わせる) |
日付型と日付型 | 日付型 |
数値型と文字列型 | エラーまたは暗黙の型変換 |
-- 正しい例:すべて文字列型で統一
CASE
WHEN status = 1 THEN '有効'
WHEN status = 2 THEN '無効'
ELSE '不明'
END
-- 問題のある例:数値と文字列が混在
CASE
WHEN status = 1 THEN 100
WHEN status = 2 THEN '無効' -- エラーの可能性
ELSE 0
END
NULL値の評価
CASE式の条件判定において、NULL値の扱いには特別な注意が必要です。SQLではNULL値は「未知の値」を表すため、通常の比較演算子(=、>など)ではNULLと正しく比較できません。NULL値を判定する場合は、IS NULLまたはIS NOT NULL演算子を使用する必要があります。
-- 誤った例:NULLとの比較
CASE
WHEN column_name = NULL THEN '該当' -- 常に偽になる
ELSE '非該当'
END
-- 正しい例:IS NULLを使用
CASE
WHEN column_name IS NULL THEN '該当'
ELSE '非該当'
END
単純CASE式における評価の特性
単純CASE式では、式の評価結果とWHEN句の値が等価(=)であるかを判定します。この場合も、NULL値の比較には注意が必要です。単純CASE式ではNULL = NULLという比較は成立しないため、NULL値を扱う場合は検索CASE式を使用することが推奨されます。
-- 単純CASE式ではNULLを正しく判定できない
CASE status
WHEN NULL THEN '未設定' -- この条件は機能しない
WHEN 1 THEN '有効'
ELSE '無効'
END
-- 検索CASE式でNULLを正しく判定
CASE
WHEN status IS NULL THEN '未設定'
WHEN status = 1 THEN '有効'
ELSE '無効'
END
複数の戻り値候補がある場合の型決定
CASE式で複数のTHEN句がある場合、データベースシステムは型の優先順位に基づいて最終的な戻り値の型を決定します。例えば、整数と小数が混在する場合は小数型が優先され、結果はすべて小数型として返されます。文字列型の場合は、最も長い文字列長に合わせて型が決定されることが一般的です。
-- 整数と小数が混在する例
SELECT
CASE
WHEN score >= 90 THEN 100
WHEN score >= 80 THEN 90.5 -- 小数が含まれる
ELSE 0
END AS adjusted_score
FROM test_results;
-- 結果はすべて小数型(100.0、90.5、0.0)となる
このように、CASE式の評価ルールと戻り値の型を正しく理解することで、予期しないエラーや誤った結果を避けることができます。特にNULL値の扱いとデータ型の統一には十分注意を払い、必要に応じて明示的な型変換を行うことが重要です。
“`html
SELECT文でのCASE式の使い方
SELECT文でCASE式を使用すると、データを取得する際に条件に応じて異なる値を返すことができます。これにより、データの表示形式を柔軟に変更したり、複雑な条件判定を実装したりすることが可能になります。ここでは、実践的なCASE式の使い方をさまざまな条件パターン別に解説していきます。
等式による条件分岐
等式を使った条件分岐は、CASE式の最も基本的な使い方です。特定の値と完全に一致するかどうかを判定し、その結果に応じて異なる値を返します。
SELECT
product_name,
category,
CASE category
WHEN '食品' THEN '生鮮食品部門'
WHEN '衣類' THEN 'アパレル部門'
WHEN '家電' THEN '電化製品部門'
ELSE 'その他部門'
END AS department
FROM products;
上記の例では、商品のカテゴリーに応じて所属部門を表示しています。単純CASE式を使用することで、簡潔に記述できます。また、検索CASE式を使って以下のように書くこともできます。
SELECT
product_name,
status,
CASE
WHEN status = 1 THEN '有効'
WHEN status = 0 THEN '無効'
ELSE '不明'
END AS status_name
FROM products;
不等式を使った条件分岐
不等式を使った条件分岐では、数値の範囲や大小関係に基づいて処理を分けることができます。これは検索CASE式でのみ実現可能で、売上ランクの判定や年齢層の分類など、実務で頻繁に使用されます。
SELECT
customer_name,
total_amount,
CASE
WHEN total_amount >= 100000 THEN 'プラチナ会員'
WHEN total_amount >= 50000 THEN 'ゴールド会員'
WHEN total_amount >= 10000 THEN 'シルバー会員'
ELSE '一般会員'
END AS member_rank
FROM customers;
CASE式は上から順に条件を評価し、最初に一致した条件の値を返します。そのため、範囲指定を行う際は、広い範囲から狭い範囲へと順番に記述する必要があります。
SELECT
product_name,
price,
CASE
WHEN price 1000 THEN '低価格帯'
WHEN price 5000 THEN '中価格帯'
WHEN price 10000 THEN '高価格帯'
ELSE 'プレミアム価格帯'
END AS price_range
FROM products;
NULL値の判定方法
データベースではNULL値の扱いが重要です。CASE式を使用することで、NULL値を判定し、適切な代替値を設定することができます。
SELECT
employee_name,
department,
CASE
WHEN department IS NULL THEN '未配属'
ELSE department
END AS department_name
FROM employees;
NULL値の判定には「=」ではなく「IS NULL」や「IS NOT NULL」を使用する必要があります。「= NULL」という記述は正しく動作しないため注意してください。
SELECT
product_name,
discount_rate,
price,
CASE
WHEN discount_rate IS NOT NULL THEN price * (1 - discount_rate)
ELSE price
END AS final_price
FROM products;
この例では、割引率がNULLでない場合は割引後の価格を計算し、NULLの場合は通常価格を返しています。NULL値を適切に処理することで、計算エラーを防ぐことができます。
LIKE句を使った曖昧検索での条件分岐
LIKE句をCASE式と組み合わせることで、文字列の部分一致や前方一致、後方一致などのパターンマッチングに基づいた条件分岐が可能になります。
SELECT
product_name,
CASE
WHEN product_name LIKE '%スマートフォン%' THEN 'モバイル機器'
WHEN product_name LIKE '%パソコン%' THEN 'PC機器'
WHEN product_name LIKE '%タブレット%' THEN 'モバイル機器'
ELSE 'その他機器'
END AS device_category
FROM products;
ワイルドカード「%」を使用することで、柔軟な文字列検索が実現できます。前方一致の場合は「キーワード%」、後方一致の場合は「%キーワード」、部分一致の場合は「%キーワード%」という形式で指定します。
SELECT
email,
CASE
WHEN email LIKE '%@gmail.com' THEN 'Gmail'
WHEN email LIKE '%@yahoo.co.jp' THEN 'Yahoo'
WHEN email LIKE '%@%co.jp' THEN '企業ドメイン'
ELSE 'その他'
END AS email_provider
FROM users;
論理演算子で複数条件を組み合わせる
AND、OR、NOTなどの論理演算子を使用することで、複数の条件を組み合わせた複雑な判定をCASE式内で実行できます。
SELECT
product_name,
price,
stock,
CASE
WHEN price >= 10000 AND stock > 0 THEN '高額在庫あり'
WHEN price >= 10000 AND stock = 0 THEN '高額在庫切れ'
WHEN price 10000 AND stock > 0 THEN '通常在庫あり'
ELSE '通常在庫切れ'
END AS product_status
FROM products;
AND演算子は全ての条件が真の場合に真となり、OR演算子はいずれかの条件が真の場合に真となります。これにより、ビジネスロジックに応じた柔軟な条件設定が可能です。
SELECT
customer_name,
age,
total_purchase,
CASE
WHEN age >= 60 OR total_purchase >= 100000 THEN '優待対象'
WHEN age >= 18 AND total_purchase >= 50000 THEN '特典対象'
ELSE '通常顧客'
END AS customer_type
FROM customers;
複数の論理演算子を組み合わせる際は、括弧を使って評価順序を明示することで、意図した条件判定を実現できます。
IN句で複数条件を指定する方法
IN句を使用すると、複数の値のいずれかに一致するかを簡潔に記述できます。複数の等式をOR演算子で繋げる代わりに、より読みやすい形式で条件を指定できます。
SELECT
product_name,
category,
CASE
WHEN category IN ('野菜', '果物', '精肉', '鮮魚') THEN '生鮮食品'
WHEN category IN ('パン', '菓子', '飲料') THEN '加工食品'
WHEN category IN ('洗剤', 'シャンプー', 'ティッシュ') THEN '日用品'
ELSE 'その他'
END AS section
FROM products;
IN句を使うことで、同じカテゴリに属する複数の値をまとめて判定できます。複数のOR条件を記述するよりもコードが簡潔になり、メンテナンス性も向上します。
SELECT
order_date,
status,
CASE
WHEN status IN ('pending', 'processing', 'shipped') THEN '処理中'
WHEN status IN ('delivered', 'completed') THEN '完了'
WHEN status IN ('cancelled', 'returned') THEN '取消・返品'
ELSE '不明'
END AS order_status_group
FROM orders;
また、NOT IN句を使用することで、特定の値に該当しない場合の処理を記述することもできます。
SELECT
employee_name,
department,
CASE
WHEN department NOT IN ('営業部', '企画部', '開発部') THEN 'バックオフィス'
ELSE 'フロントオフィス'
END AS office_type
FROM employees;
“`
CASE式の応用テクニック
CASE式の基本的な使い方をマスターしたら、次は実務で役立つ応用テクニックに挑戦しましょう。ここでは、より複雑な条件分岐や集計処理を実現するための高度な技法を解説します。これらのテクニックを使いこなすことで、複雑なビジネスロジックをシンプルなSQL文で表現できるようになります。
CASE式のネスト(入れ子)構造
CASE式は、他のCASE式の中に入れ子にすることができます。この技法を使うことで、多段階の条件分岐を実現できます。例えば、まず大分類で条件分岐し、その結果によってさらに細かい分類を行うといった処理が可能になります。
以下は、商品の価格帯と在庫状況を組み合わせて、販売戦略を判定する例です。
SELECT
product_name,
price,
stock,
CASE
WHEN price >= 10000 THEN
CASE
WHEN stock > 50 THEN '高価格帯・在庫豊富'
WHEN stock > 10 THEN '高価格帯・在庫適正'
ELSE '高価格帯・在庫少'
END
WHEN price >= 5000 THEN
CASE
WHEN stock > 50 THEN '中価格帯・在庫豊富'
WHEN stock > 10 THEN '中価格帯・在庫適正'
ELSE '中価格帯・在庫少'
END
ELSE
CASE
WHEN stock > 50 THEN '低価格帯・在庫豊富'
WHEN stock > 10 THEN '低価格帯・在庫適正'
ELSE '低価格帯・在庫少'
END
END AS sales_strategy
FROM products;
このように、外側のCASE式で価格帯を判定し、内側のCASE式で在庫状況を判定することで、複数の基準を組み合わせた分類が可能になります。ただし、ネストが深くなりすぎると可読性が低下するため、3階層程度までに留めることをおすすめします。
また、ネスト構造を使う代わりに、検索CASE式でAND条件を組み合わせる方法もあります。状況に応じて読みやすい方を選択しましょう。
区分ごとのデータ集計
CASE式は集計関数と組み合わせることで、データを区分ごとに分類しながら集計する強力な手法となります。この技法を使えば、複数のSELECT文を実行せずとも、1つのクエリで多角的な集計結果を取得できます。
以下は、売上データを年代別に集計する例です。
SELECT
COUNT(CASE WHEN age 20 THEN 1 END) AS teen_count,
COUNT(CASE WHEN age >= 20 AND age 30 THEN 1 END) AS twenties_count,
COUNT(CASE WHEN age >= 30 AND age 40 THEN 1 END) AS thirties_count,
COUNT(CASE WHEN age >= 40 THEN 1 END) AS over_forty_count,
SUM(CASE WHEN age 20 THEN sales_amount ELSE 0 END) AS teen_sales,
SUM(CASE WHEN age >= 20 AND age 30 THEN sales_amount ELSE 0 END) AS twenties_sales,
SUM(CASE WHEN age >= 30 AND age 40 THEN sales_amount ELSE 0 END) AS thirties_sales,
SUM(CASE WHEN age >= 40 THEN sales_amount ELSE 0 END) AS over_forty_sales
FROM sales;
この例では、顧客の年齢に応じて売上件数と売上金額を同時に集計しています。CASE式を使うことで、縦に並んでいるデータを横方向に展開するピボット集計が実現できます。
さらに、GROUP BY句と組み合わせることで、より詳細な分析が可能になります。
SELECT
region,
SUM(CASE WHEN product_category = '家電' THEN sales_amount ELSE 0 END) AS electronics_sales,
SUM(CASE WHEN product_category = '衣料品' THEN sales_amount ELSE 0 END) AS clothing_sales,
SUM(CASE WHEN product_category = '食品' THEN sales_amount ELSE 0 END) AS food_sales
FROM sales
GROUP BY region;
このクエリでは、地域ごとにカテゴリ別の売上を集計しています。データ分析やレポート作成において、このテクニックは非常に有用です。
条件に合致するデータのみを集計する方法
CASE式を使うと、特定の条件を満たすデータのみを選択的に集計することができます。これは、通常のWHERE句では実現できない柔軟な集計処理を可能にします。WHERE句がレコード全体をフィルタリングするのに対し、CASE式は集計対象となる値を選択的に処理できるという違いがあります。
例えば、合格者の平均点と不合格者の平均点を同時に計算する場合、以下のように記述できます。
SELECT
AVG(CASE WHEN score >= 60 THEN score END) AS pass_average,
AVG(CASE WHEN score 60 THEN score END) AS fail_average,
MAX(CASE WHEN score >= 60 THEN score END) AS pass_max,
MIN(CASE WHEN score 60 THEN score END) AS fail_min
FROM test_results;
この例では、合格点以上と未満で異なる集計を行っています。CASE式の条件に該当しない場合はNULLが返されるため、集計関数はその値を無視します。この特性を利用することで、条件別の集計を1つのクエリで実行できます。
さらに、複雑な条件での絞り込み集計も可能です。
SELECT
department,
COUNT(*) AS total_employees,
AVG(CASE
WHEN experience_years >= 5 AND performance_rating >= 4
THEN salary
END) AS senior_high_performer_avg_salary,
COUNT(CASE
WHEN experience_years 2 AND hire_date >= '2023-01-01'
THEN 1
END) AS new_hire_count
FROM employees
GROUP BY department;
この例では、部署ごとに全従業員数を数えつつ、経験年数5年以上かつ評価が高い従業員の平均給与や、新入社員の数を同時に集計しています。このように、CASE式を使わない場合は複数のクエリやサブクエリが必要になる処理を、シンプルな1つのSQLで実現できます。
条件付き集計は、ビジネスインテリジェンスやデータ分析において頻繁に使用される重要なテクニックです。集計関数との組み合わせパターンを理解することで、データベースから効率的に必要な情報を引き出せるようになります。
他のSQL句との組み合わせ
CASE式はSELECT文の列として使うだけでなく、SQLの様々な句と組み合わせることで、より柔軟で高度なデータ操作が可能になります。WHERE句での絞り込み条件、ORDER BY句での並び替え基準、HAVING句でのグループ化後のフィルタリング、SET句での更新値の決定など、多様な場面でCASE式を活用することで、複雑なビジネスロジックをSQL内で完結させることができます。ここでは、CASE式と他のSQL句を組み合わせた実践的な使い方について詳しく解説していきます。
WHERE句でCASE式を活用する
WHERE句にCASE式を組み込むことで、条件によって異なるフィルタリングロジックを動的に適用することができます。特に、パラメータの値によって検索条件を変えたい場合や、複雑な条件分岐を一つのクエリで表現したい場合に有効です。
例えば、ユーザーの権限レベルに応じて異なるデータを表示する場合、以下のようにCASE式を活用できます。
SELECT
employee_id,
employee_name,
salary
FROM
employees
WHERE
CASE
WHEN @user_role = 'admin' THEN 1
WHEN @user_role = 'manager' AND department_id = @user_department THEN 1
WHEN @user_role = 'employee' AND employee_id = @user_id THEN 1
ELSE 0
END = 1;
このクエリでは、ユーザーの役割(@user_role)に応じて、表示できる従業員データの範囲を制御しています。管理者は全データ、マネージャーは自部署のみ、一般従業員は自分のデータのみが閲覧できるようになります。
また、検索条件の有無によって動的にフィルタリングを適用する場合にも便利です。
SELECT
product_id,
product_name,
price
FROM
products
WHERE
price >= CASE
WHEN @min_price IS NOT NULL THEN @min_price
ELSE 0
END
AND price = CASE
WHEN @max_price IS NOT NULL THEN @max_price
ELSE 999999
END;
この例では、最小価格や最大価格のパラメータが指定されていない場合でも、適切なデフォルト値を設定することで、クエリを動的に動作させています。
ORDER BY句での並び替えに使う
ORDER BY句にCASE式を適用することで、条件に応じた柔軟な並び替えロジックを実現できます。通常の昇順・降順だけでは表現できない、ビジネスルールに基づいた優先順位付けが可能になります。
例えば、注文ステータスに応じて優先度を設定し、その優先度順に並び替える場合は以下のようになります。
SELECT
order_id,
customer_name,
order_status,
order_date
FROM
orders
ORDER BY
CASE order_status
WHEN '緊急' THEN 1
WHEN '処理中' THEN 2
WHEN '保留' THEN 3
WHEN '完了' THEN 4
ELSE 5
END,
order_date DESC;
このクエリでは、まず注文ステータスの優先度でソートし、同じステータス内では注文日の新しい順に並び替えています。緊急の注文が最優先で表示されるため、業務の優先順位が視覚的に明確になります。
また、複数の列を組み合わせた複雑な並び替え条件も表現できます。
SELECT
employee_id,
employee_name,
department,
performance_score
FROM
employees
ORDER BY
CASE
WHEN department = '営業部' AND performance_score >= 90 THEN 1
WHEN department = '営業部' AND performance_score >= 70 THEN 2
WHEN department = '技術部' AND performance_score >= 90 THEN 3
WHEN department = '技術部' AND performance_score >= 70 THEN 4
ELSE 5
END,
employee_name;
このように、部署と評価スコアを組み合わせた独自の優先順位でデータを並び替えることができ、人事評価や表彰対象者の選定などに活用できます。
HAVING句でのグループ化後の条件指定
HAVING句とCASE式を組み合わせることで、GROUP BYで集計した後のデータに対して、条件分岐を含む複雑なフィルタリングを行うことができます。集計結果に対する動的な条件設定が必要な場面で特に有効です。
例えば、部署ごとの平均給与を算出し、部署の種類によって異なる基準で絞り込む場合は以下のようになります。
SELECT
department,
AVG(salary) as avg_salary,
COUNT(*) as employee_count
FROM
employees
GROUP BY
department
HAVING
CASE
WHEN department IN ('営業部', 'マーケティング部') THEN AVG(salary) >= 5000000
WHEN department IN ('技術部', '開発部') THEN AVG(salary) >= 6000000
WHEN department = '管理部' THEN AVG(salary) >= 4500000
ELSE AVG(salary) >= 4000000
END;
このクエリでは、部署の特性に応じて異なる平均給与の基準を設定し、その基準を満たす部署のみを抽出しています。業種や職種による給与水準の違いを考慮した分析が可能になります。
また、複数の集計条件を組み合わせた高度なフィルタリングも実現できます。
SELECT
product_category,
SUM(sales_amount) as total_sales,
COUNT(DISTINCT customer_id) as unique_customers
FROM
sales
GROUP BY
product_category
HAVING
CASE
WHEN SUM(sales_amount) > 10000000 THEN COUNT(DISTINCT customer_id) >= 100
WHEN SUM(sales_amount) > 5000000 THEN COUNT(DISTINCT customer_id) >= 50
ELSE COUNT(DISTINCT customer_id) >= 20
END;
この例では、売上総額の規模に応じて、必要な顧客数の基準を変動させています。売上規模が大きいカテゴリほど、より多くの顧客基盤を持つことを条件としており、ビジネスの健全性を評価する指標として活用できます。
SET句での変数代入に利用する
SET句でCASE式を使用することで、条件に応じて異なる値を変数に代入することができます。主にストアドプロシージャや変数を使った処理において、動的な値の設定が必要な場合に活用されます。
例えば、売上実績に基づいて歩合率を決定する変数を設定する場合は以下のようになります。
DECLARE @commission_rate DECIMAL(5,2);
SET @commission_rate = CASE
WHEN @monthly_sales >= 10000000 THEN 0.15
WHEN @monthly_sales >= 5000000 THEN 0.10
WHEN @monthly_sales >= 3000000 THEN 0.07
ELSE 0.05
END;
SELECT
employee_id,
employee_name,
@monthly_sales as sales,
@monthly_sales * @commission_rate as commission
FROM
employees
WHERE
employee_id = @target_employee;
このコードでは、月次売上の金額に応じて段階的に歩合率を設定し、その値を使って実際の歩合給を計算しています。条件分岐が明確で、メンテナンス性の高いコードになります。
また、複数の条件を評価して、適切なステータスコードを変数に設定する場合も便利です。
DECLARE @process_status VARCHAR(20);
SET @process_status = CASE
WHEN @error_count > 0 THEN 'ERROR'
WHEN @warning_count > 5 THEN 'WARNING'
WHEN @processed_records = @total_records THEN 'COMPLETED'
WHEN @processed_records > 0 THEN 'IN_PROGRESS'
ELSE 'PENDING'
END;
-- ステータスに応じた後続処理
IF @process_status = 'ERROR'
BEGIN
-- エラー処理
END;
この例では、処理の進行状況や結果を評価して適切なステータスを設定し、そのステータスに基づいて後続の処理を分岐させています。ただし、CASE式の評価は上から順に行われるため、条件の記述順序には注意が必要です。より具体的な条件を先に記述し、一般的な条件を後に配置することで、意図した通りの動作を実現できます。
UPDATE文でのCASE式の使い方
UPDATE文にCASE式を組み合わせることで、条件に応じて異なる更新値を設定できる強力な機能を実現できます。複数の条件ごとに別々のUPDATE文を実行する必要がなくなり、一度のクエリで効率的にデータを更新できるのが大きなメリットです。
UPDATE文でCASE式を使用する基本的な構文は次のとおりです。SET句の中にCASE式を記述し、条件に応じた更新値を指定します。
UPDATE テーブル名
SET カラム名 = CASE
WHEN 条件1 THEN 値1
WHEN 条件2 THEN 値2
ELSE デフォルト値
END
WHERE 更新対象の条件;
具体的な使用例として、商品テーブルの価格を在庫状況に応じて更新するケースを見てみましょう。在庫が少ない商品は値上げし、在庫が多い商品は値下げするといった処理を一度に実行できます。
UPDATE products
SET price = CASE
WHEN stock 10 THEN price * 1.1
WHEN stock > 100 THEN price * 0.9
ELSE price
END
WHERE category = '電化製品';
この例では、在庫が10未満の商品は10%値上げ、在庫が100を超える商品は10%値下げされ、それ以外は価格が据え置かれます。
複数のカラムを同時に更新することもCASE式を使えば簡単です。各カラムにそれぞれCASE式を適用することで、複雑な更新ロジックを実装できます。
UPDATE employees
SET
salary = CASE
WHEN performance_score >= 90 THEN salary * 1.15
WHEN performance_score >= 70 THEN salary * 1.10
ELSE salary * 1.05
END,
bonus = CASE
WHEN performance_score >= 90 THEN 500000
WHEN performance_score >= 70 THEN 300000
ELSE 100000
END
WHERE employment_type = '正社員';
この例では、従業員の評価スコアに応じて給与とボーナスを同時に更新しています。評価が90以上なら給与15%増と50万円のボーナス、70以上なら10%増と30万円のボーナスといった形で、きめ細かい条件分岐が可能です。
UPDATE文でCASE式を使う際の実践的なテクニックとして、既存の値を参照して更新する方法があります。現在の値に基づいて新しい値を算出する処理は、業務システムで頻繁に発生します。
UPDATE orders
SET status = CASE
WHEN payment_date IS NOT NULL AND ship_date IS NULL THEN '発送待ち'
WHEN ship_date IS NOT NULL AND delivery_date IS NULL THEN '配送中'
WHEN delivery_date IS NOT NULL THEN '配送完了'
ELSE '支払い待ち'
END;
また、特定の条件に該当する行のみを更新し、それ以外は現在の値を保持したい場合は、ELSE句に該当カラムを指定します。
UPDATE products
SET discount_rate = CASE
WHEN release_date CURRENT_DATE - INTERVAL '1 year' THEN 0.3
WHEN release_date CURRENT_DATE - INTERVAL '6 months' THEN 0.2
ELSE discount_rate
END;
この方法により、発売から1年以上経過した商品は30%引き、6ヶ月以上は20%引きとし、それ以外の商品は既存の割引率を維持できます。
注意点として、WHERE句を省略すると全レコードが更新対象になるため、意図しない大量更新を防ぐために必ず適切な条件を指定しましょう。また、CASE式の評価結果とカラムのデータ型が一致しない場合はエラーが発生するため、型の整合性にも注意が必要です。
使用場面 | メリット |
---|---|
条件による価格変更 | 複数のUPDATE文を1つにまとめられる |
ステータスの一括更新 | 処理速度が向上し、トランザクション管理が簡単 |
複数カラムの同時更新 | データの整合性を保ちながら更新可能 |
UPDATE文とCASE式の組み合わせは、データメンテナンスの効率を大幅に向上させる強力な手法です。条件分岐を含む更新処理を簡潔に記述でき、コードの可読性とパフォーマンスの両面で優れた結果をもたらします。
CASE式とIF文の違いと使い分け
SQLで条件分岐を扱う際、「CASE式」と「IF文」のどちらを使うべきか迷うことがあるかもしれません。両者は似た機能を持ちながらも、使用できる場面や特性が異なります。ここでは、それぞれの特徴と適切な使い分けについて詳しく解説します。
CASE式とIF文の基本的な違い
まず理解しておくべき重要な点は、CASE式はSQL標準の機能であり、ほぼすべてのデータベース管理システムで使用できるという点です。一方、IF文はMySQLなど一部のデータベースでのみ使用可能な独自の関数です。
項目 | CASE式 | IF文 |
---|---|---|
対応データベース | SQL標準(Oracle、PostgreSQL、MySQL、SQL Serverなど) | MySQL、MariaDBなど限定的 |
構文の複雑さ | やや冗長だが明確 | シンプルだが条件が限定的 |
条件分岐の数 | 複数条件に対応 | 単一条件(2択) |
可読性 | 条件が複雑でも読みやすい | 単純な条件では読みやすい |
使用可能な場所 | SELECT、WHERE、ORDER BYなど幅広く | 主にSELECT句 |
IF文の構文と特徴
MySQLで使用できるIF文は、非常にシンプルな構文を持っています。基本的な書き方は以下の通りです。
IF(条件式, 真の場合の値, 偽の場合の値)
例えば、商品の在庫状況を判定する場合は次のように記述します。
SELECT
product_name,
stock,
IF(stock > 0, '在庫あり', '在庫なし') AS stock_status
FROM
products;
IF文は単純な2択の条件分岐に適しており、コードがコンパクトになるというメリットがあります。ただし、3つ以上の条件を扱う場合はIF文をネストする必要があり、可読性が低下します。
CASE式とIF文の使い分け基準
実際の開発現場では、以下の基準で使い分けることが推奨されます。
CASE式を使うべき場面
- 複数のデータベースシステムで動作させる必要がある場合(ポータビリティ重視)
- 3つ以上の条件分岐が必要な場合
- 複雑な条件式を扱う場合(AND、ORなどの組み合わせ)
- チームでの開発でSQL標準に準拠したい場合
- WHERE句やORDER BY句など、様々な場所で条件分岐を使いたい場合
-- 複数条件のCASE式の例
SELECT
product_name,
price,
CASE
WHEN price 1000 THEN '低価格'
WHEN price BETWEEN 1000 AND 5000 THEN '標準価格'
WHEN price > 5000 THEN '高価格'
ELSE '未設定'
END AS price_category
FROM
products;
IF文を使うべき場面
- MySQLまたはMariaDBのみで動作することが確定している場合
- 単純な2択の条件分岐のみの場合
- コードの簡潔性を優先したい場合
- パフォーマンスをわずかでも最適化したい場合(IF文の方が若干高速なケースがある)
-- 単純な条件のIF文の例
SELECT
customer_name,
IF(total_purchase > 10000, '優良顧客', '一般顧客') AS customer_type
FROM
customers;
パフォーマンスの違い
パフォーマンス面では、単純な2択の条件分岐の場合、IF文の方がわずかに高速に動作することがあります。しかし、この差は通常のクエリでは体感できないレベルであり、可読性や保守性を犠牲にしてまでIF文を選択する理由にはなりません。
むしろ、複雑な条件分岐でIF文をネストした場合、データベースの最適化が効きにくくなり、CASE式を使った方が結果的にパフォーマンスが良くなるケースもあります。
実践的な推奨事項
実務において推奨されるアプローチは以下の通りです。
- 基本的にはCASE式を使用することで、SQL標準に準拠し、データベースの移行や変更に強いコードを書く
- MySQL専用で開発しており、単純な2択の条件のみの場合はIF文の使用も検討する
- チーム開発の場合は、コーディング規約でどちらを使うか統一しておく
- 既存のコードベースがある場合は、それに合わせて統一性を保つ
最終的には、ポータビリティと可読性を重視するならCASE式、MySQL限定で簡潔さを求めるならIF文という判断基準を持っておくと良いでしょう。ただし、迷った場合はSQL標準であるCASE式を選択することが、長期的な保守性の観点から推奨されます。
“`html
CASE式を使用する際の注意点とポイント
CASE式は非常に便利な機能ですが、適切に使用しないとパフォーマンスの低下やエラーの原因となることがあります。ここでは、実務でCASE式を使用する際に押さえておくべき重要な注意点とポイントについて解説します。
データ型の統一
CASE式では、すべてのTHEN句とELSE句で返される値のデータ型を統一する必要があります。異なるデータ型が混在すると、暗黙的な型変換が発生したり、エラーが発生する可能性があります。
-- 誤った例:文字列と数値が混在
SELECT
CASE
WHEN score >= 80 THEN '優秀'
WHEN score >= 60 THEN 60 -- エラーの原因
ELSE '不可'
END AS result
FROM students;
-- 正しい例:すべて文字列型で統一
SELECT
CASE
WHEN score >= 80 THEN '優秀'
WHEN score >= 60 THEN '60点以上'
ELSE '不可'
END AS result
FROM students;
特にNULL値を返す場合は、適切にCASTやCONVERT関数を使用してデータ型を明示的に指定することをおすすめします。
ELSE句の指定
ELSE句を省略した場合、どの条件にも該当しないレコードはNULLが返されます。意図しないNULL値を避けるためにも、明示的にELSE句を記述することがベストプラクティスです。
-- ELSE句なし:条件に合わない場合はNULLになる
SELECT
name,
CASE
WHEN age 20 THEN '未成年'
WHEN age 65 THEN '成人'
END AS age_group
FROM users;
-- ELSE句あり:すべてのケースを明示的に処理
SELECT
name,
CASE
WHEN age 20 THEN '未成年'
WHEN age 65 THEN '成人'
ELSE '高齢者'
END AS age_group
FROM users;
条件の評価順序
CASE式は上から順番に条件を評価し、最初に真となった条件のTHEN句を実行して終了します。後続の条件は評価されないため、条件の記述順序が非常に重要です。
-- 誤った順序:すべて「低い」になってしまう
SELECT
CASE
WHEN price > 0 THEN '低い' -- 先に評価される
WHEN price > 1000 THEN '普通'
WHEN price > 5000 THEN '高い'
END AS price_range
FROM products;
-- 正しい順序:条件は厳しいものから記述
SELECT
CASE
WHEN price > 5000 THEN '高い'
WHEN price > 1000 THEN '普通'
WHEN price > 0 THEN '低い'
ELSE '無料'
END AS price_range
FROM products;
パフォーマンスへの影響
CASE式を複雑にしすぎると、クエリのパフォーマンスに影響を与える可能性があります。以下のポイントに注意しましょう。
- WHERE句での使用を検討:CASE式をSELECT句で使用する前に、WHERE句でデータを絞り込むことでパフォーマンスを向上できる場合があります
- インデックスの活用制限:CASE式の条件部分にカラムを使用すると、インデックスが効かなくなる場合があります
- 過度なネスト回避:CASE式を何重にも入れ子にすると可読性が低下し、実行速度も遅くなります
- サブクエリの多用に注意:CASE式の中で複数のサブクエリを使用すると、著しくパフォーマンスが低下する可能性があります
NULL値の扱い
NULL値とCASE式を組み合わせる際は、特別な注意が必要です。NULL値は通常の等号(=)では比較できず、IS NULLまたはIS NOT NULLを使用する必要があります。
-- 誤った例:NULLは=では判定できない
SELECT
CASE
WHEN email = NULL THEN '未登録' -- これは機能しない
ELSE '登録済み'
END AS email_status
FROM users;
-- 正しい例:IS NULLを使用
SELECT
CASE
WHEN email IS NULL THEN '未登録'
ELSE '登録済み'
END AS email_status
FROM users;
可読性とメンテナンス性
複雑なビジネスロジックをCASE式で実装する場合は、可読性を保つことが重要です。
- 適切なインデント:WHEN句とTHEN句を見やすく整形し、条件が一目でわかるようにします
- コメントの追加:複雑な条件分岐には、なぜその条件が必要なのかコメントを残しましょう
- ビューやストアドプロシージャの活用:複雑なCASE式は、ビューやストアドプロシージャに切り出すことで再利用性が高まります
- 命名規則の統一:CASE式で生成するカラムには、わかりやすいエイリアス名を付けます
データベース製品による差異
CASE式は標準SQLの機能ですが、データベース製品によって細かい仕様が異なる場合があります。MySQL、PostgreSQL、Oracle、SQL Serverなど、使用するデータベースのドキュメントを確認し、固有の制約や拡張機能を把握しておくことが大切です。特に文字列の長さ制限やデータ型の暗黙的変換ルールには注意が必要です。
デバッグとテスト
CASE式を含むクエリは、すべての条件分岐パターンをテストすることが重要です。
- 各WHEN句の条件に該当するテストデータを用意する
- ELSE句が実行されるケースも確認する
- NULL値を含むデータでの動作を検証する
- 境界値(範囲の最小値・最大値)での挙動をチェックする
これらの注意点を押さえることで、CASE式を安全かつ効果的に活用できます。実務では、シンプルで理解しやすいクエリを心がけることが、長期的な保守性とパフォーマンスの両立につながります。
“`
“`html
まとめ
SQL CASE式は、条件に応じてデータを動的に変換・分岐できる強力な機能です。本記事では、CASE式の基本から応用まで幅広く解説してきました。
CASE式には単純CASE式と検索CASE式の2つの構文があり、用途に応じて使い分けることが重要です。単純CASE式は特定のカラムの値を比較する際にシンプルに記述でき、検索CASE式は複雑な条件や不等式、NULL判定など柔軟な条件分岐を実現できます。
SELECT文での活用では、等式・不等式・LIKE句・IN句など様々な条件指定が可能で、論理演算子を組み合わせることでより高度な判定も行えます。また、CASE式のネスト構造や集計関数との組み合わせにより、区分ごとのデータ集計や条件付き集計といった応用テクニックも実装できます。
さらに、WHERE句、ORDER BY句、HAVING句、SET句など他のSQL句と組み合わせることで、データの抽出・並び替え・グループ化・更新処理においても柔軟な制御が可能になります。UPDATE文でのCASE式活用により、複数行を条件に応じて一括更新することもできます。
CASE式を使用する際は、以下のポイントを押さえておきましょう。
- データ型の一貫性を保ち、全ての分岐で同じ型を返すようにする
- ELSE句を省略するとNULLが返されるため、明示的に指定することを推奨
- 条件の評価順序を意識し、より具体的な条件を先に記述する
- 可読性を保つため、複雑すぎるネストは避け、適度に分割する
注意点として、一部のデータベースシステムではIF文が使える場合もありますが、CASE式はSQL標準規格に準拠しており、様々なDBMSで互換性が高いという利点があります。移植性を重視する場合はCASE式の使用が推奨されます。
CASE式をマスターすることで、SQLクエリの表現力が大きく向上し、複雑なビジネスロジックもデータベース側で効率的に処理できるようになります。日々の業務でのデータ分析やレポート作成において、CASE式は必須のスキルといえるでしょう。
“`