【SQLite】CASE文の書き方と具体的な使用例 – 条件に応じたデータ変換と制御
はじめに
データベースを操作していると、「このカラムの値がAの場合はX、Bの場合はY、それ以外の場合はZと表示したい」「特定の条件を満たす行だけを集計したい」「条件によって更新する値を分けたい」といった、条件に応じたデータの加工や制御が必要になる場面が頻繁にあります。SQLにおいて、このような条件分岐を実現するための強力な機能がCASE文です。
CASE文は、クエリ内で条件に基づいて値を返す式です。プログラミング言語におけるif-then-elseやswitch文のようなものと考えると分かりやすいでしょう。SQLiteを含む多くのリレーショナルデータベースシステムでサポートされており、データの変換、集計、並べ替え、更新など、様々な操作で活用できます。
本記事では、SQLiteにおけるCASE文の基本的な構文から、二つの異なる形式(シンプルCASEとSearched CASE)の詳細、そして実際のデータベース操作における様々な具体的な使用例までを網羅的に解説します。約5000語のボリュームで、初心者の方でもCASE文を理解し、実践的なSQL記述に活かせるようになることを目指します。
CASE文の基本的な構文
SQLiteのCASE文には、主に以下の二つの形式があります。
-
シンプルCASE式 (Simple CASE Expression)
特定のカラムや式の値に基づいて分岐する場合に使用します。sql
CASE expression
WHEN value1 THEN result1
[WHEN value2 THEN result2]
[...]
[ELSE result_else]
ENDCASE expression
: 評価対象となるカラム名または式を指定します。WHEN valueN
:expression
の値と比較する値を指定します。THEN resultN
:expression
がvalueN
と一致した場合に返される値を指定します。ELSE result_else
: どのWHEN
条件にも一致しなかった場合に返される値を指定します。ELSE
節を省略した場合、どのWHEN
条件にも一致しない場合はNULL
が返されます。END
: CASE文の終わりを示します。必須です。
-
Searched CASE式 (Searched CASE Expression)
より複雑な条件や複数のカラムを組み合わせた条件に基づいて分岐する場合に使用します。シンプルCASEよりも柔軟性が高いです。sql
CASE
WHEN condition1 THEN result1
[WHEN condition2 THEN result2]
[...]
[ELSE result_else]
ENDCASE
: 式の開始を示します。シンプルCASEのように評価対象の式は指定しません。WHEN conditionN
: 真偽値となる条件式(例:column > 10
,column1 = column2
,column IS NULL
など)を指定します。THEN resultN
:conditionN
が真 (TRUE
) と評価された場合に返される値を指定します。ELSE result_else
: どのWHEN
条件も真にならなかった場合に返される値を指定します。ELSE
節を省略した場合、どのWHEN
条件も真にならない場合はNULL
が返されます。END
: CASE文の終わりを示します。必須です。
CASE文の評価の流れ:
どちらの形式も、記述されたWHEN
節を上から順番に評価していきます。
- シンプルCASE:
CASE
の直後に指定したexpression
の値と、各WHEN
節のvalueN
を比較します。最初に一致が見つかったWHEN
節に対応するresultN
が返され、そこで評価は終了します。以降のWHEN
節は評価されません。 - Searched CASE: 各
WHEN
節のconditionN
が真 (TRUE
) となるかを評価します。最初に真となったconditionN
に対応するresultN
が返され、そこで評価は終了します。以降のWHEN
節は評価されません。
どのWHEN
条件も満たされなかった場合、ELSE
節が記述されていればresult_else
が返されます。ELSE
節が省略されている場合はNULL
が返されます。
これらの基本的な構文を理解した上で、具体的な使用例を見ていきましょう。
シンプルCASE文の詳細と使用例
シンプルCASE文は、単一の値に基づいて処理を分岐させたい場合に簡潔に記述できる形式です。特定のステータスコードを対応する文字列に変換したり、固定値のリストに基づいて分類したりするのに適しています。
構文:
sql
CASE expression
WHEN value1 THEN result1
[WHEN value2 THEN result2]
...
[ELSE result_else]
END
評価の仕組み:
expression
の値と、各WHEN valueN
で指定された値を順に比較します。最初の合致が見つかったTHEN resultN
の値が戻り値となります。
具体的な使用例:
簡単な例として、商品のカテゴリコードを対応するカテゴリ名に変換するケースを考えます。
まず、サンプルデータを作成します。
“`sql
— サンプルテーブル作成
CREATE TABLE products (
id INTEGER PRIMARY KEY,
name TEXT NOT NULL,
category_code INTEGER
);
— サンプルデータ挿入
INSERT INTO products (name, category_code) VALUES
(‘Laptop’, 1),
(‘Keyboard’, 2),
(‘Mouse’, 2),
(‘Monitor’, 1),
(‘Tablet’, 3),
(‘Speaker’, 99), — 未知のカテゴリ
(‘Webcam’, NULL); — カテゴリ不明
“`
このproducts
テーブルに対して、category_code
を分かりやすいカテゴリ名に変換するクエリを実行します。
sql
SELECT
name,
category_code,
CASE category_code
WHEN 1 THEN 'Electronics'
WHEN 2 THEN 'Accessory'
WHEN 3 THEN 'Mobile Device'
ELSE 'Other' -- どのWHENにも一致しない場合
END AS category_name
FROM products;
実行結果:
name | category_code | category_name |
---|---|---|
Laptop | 1 | Electronics |
Keyboard | 2 | Accessory |
Mouse | 2 | Accessory |
Monitor | 1 | Electronics |
Tablet | 3 | Mobile Device |
Speaker | 99 | Other |
Webcam | NULL | Other |
解説:
CASE category_code
:category_code
カラムの値を評価対象とします。WHEN 1 THEN 'Electronics'
:category_code
が1ならば’Electronics’を返します。WHEN 2 THEN 'Accessory'
:category_code
が2ならば’Accessory’を返します。WHEN 3 THEN 'Mobile Device'
:category_code
が3ならば’Mobile Device’を返します。ELSE 'Other'
: 上記のどの値にも一致しなかった場合(例えば99やNULL)、’Other’を返します。END AS category_name
: CASE文の結果にcategory_name
という別名をつけます。
この例のように、シンプルCASEは固定の値リストに基づくマッピングに非常に有効です。
ELSE
節を省略した場合:
もしELSE
節を省略すると、どのWHEN
条件にも一致しなかった場合はNULL
が返されます。
sql
SELECT
name,
category_code,
CASE category_code
WHEN 1 THEN 'Electronics'
WHEN 2 THEN 'Accessory'
WHEN 3 THEN 'Mobile Device'
-- ELSE節を省略
END AS category_name_or_null
FROM products;
実行結果:
name | category_code | category_name_or_null |
---|---|---|
Laptop | 1 | Electronics |
Keyboard | 2 | Accessory |
Mouse | 2 | Accessory |
Monitor | 1 | Electronics |
Tablet | 3 | Mobile Device |
Speaker | 99 | NULL |
Webcam | NULL | NULL |
解説:
category_code
が99の’Speaker’とNULLの’Webcam’は、どのWHEN
条件にも一致しないため、結果がNULL
になっています。意図しないNULL
を防ぐため、多くの場合ELSE
節を明示的に記述することが推奨されます。
NULL値の比較に関する注意点:
SQLにおいて、NULL
値との比較は特殊です。例えば、column = NULL
という条件は通常NULL
または偽 (FALSE
) と評価され、真 (TRUE
) となることはありません。NULL
との比較にはIS NULL
またはIS NOT NULL
を使用します。
シンプルCASE文のWHEN valueN
の部分では、valueN
にリテラル値や他のカラム名を指定しますが、WHEN NULL
という記述はできません。シンプルCASEのexpression WHEN valueN
の評価は内部的にexpression = valueN
のような比較で行われるため、expression
がNULL
の場合、どのWHEN valueN
条件とも一致しません(NULL = valueN
は真にならないため)。
もし評価対象のexpression
がNULL
である場合に特定の値を返したい場合は、シンプルCASEではELSE
節で処理するか、あるいはSearched CASEを使用する必要があります。
上記の例で、category_code
がNULL
の場合に特定の文字列(例: ‘Unknown Category’)を返したい場合、シンプルCASEではELSE
で処理するのが一般的です。
sql
SELECT
name,
category_code,
CASE category_code
WHEN 1 THEN 'Electronics'
WHEN 2 THEN 'Accessory'
WHEN 3 THEN 'Mobile Device'
ELSE 'Unknown Category' -- NULLの場合もここで捕捉
END AS category_name
FROM products;
実行結果:
name | category_code | category_name |
---|---|---|
Laptop | 1 | Electronics |
Keyboard | 2 | Accessory |
Mouse | 2 | Accessory |
Monitor | 1 | Electronics |
Tablet | 3 | Mobile Device |
Speaker | 99 | Unknown Category |
Webcam | NULL | Unknown Category |
このように、シンプルCASEで評価対象がNULLの場合、ELSE
節で捕捉されます。Searched CASEを使えば、WHEN column IS NULL THEN ...
のように明示的にNULL条件を記述できます。
シンプルCASEは、等価比較による分岐に適しており、コードが簡潔になるため可読性が高いという利点があります。
Searched CASE文の詳細と使用例
Searched CASE文は、シンプルCASEよりも柔軟な条件指定が可能です。比較演算子(>, <, >=, <=, <>, !=)、論理演算子(AND, OR, NOT)、そしてIS NULL
, BETWEEN
, IN
, LIKE
など、SQLの様々な条件式をWHEN
節に記述できます。複数のカラムを組み合わせた条件や、値の範囲による分岐、複雑なビジネスルールに基づく処理など、多岐にわたるケースで活躍します。
構文:
sql
CASE
WHEN condition1 THEN result1
[WHEN condition2 THEN result2]
...
[ELSE result_else]
END
評価の仕組み:
各WHEN conditionN
の条件式を上から順に評価します。最初に条件が真 (TRUE
) となったTHEN resultN
の値が戻り値となり、そこで評価は終了します。以降のWHEN
節は評価されません。
具体的な使用例:
ユーザーの年齢に基づいて、ライフステージを分類する例を考えます。年齢は数値の範囲で定義されるため、Searched CASEが適しています。
まず、サンプルデータを作成します。
“`sql
— サンプルテーブル作成
CREATE TABLE users (
id INTEGER PRIMARY KEY,
name TEXT NOT NULL,
age INTEGER
);
— サンプルデータ挿入
INSERT INTO users (name, age) VALUES
(‘Alice’, 5),
(‘Bob’, 15),
(‘Charlie’, 30),
(‘David’, 65),
(‘Eve’, 80),
(‘Frank’, NULL); — 年齢不明
“`
このusers
テーブルに対して、年齢からライフステージを判断するクエリを実行します。
sql
SELECT
name,
age,
CASE
WHEN age IS NULL THEN 'Unknown Age' -- ageがNULLの場合
WHEN age < 13 THEN 'Child' -- ageが13未満
WHEN age < 20 THEN 'Teenager' -- ageが20未満 (13以上19以下)
WHEN age < 60 THEN 'Adult' -- ageが60未満 (20以上59以下)
ELSE 'Senior' -- それ以外 (60以上)
END AS life_stage
FROM users;
実行結果:
name | age | life_stage |
---|---|---|
Alice | 5 | Child |
Bob | 15 | Teenager |
Charlie | 30 | Adult |
David | 65 | Senior |
Eve | 80 | Senior |
Frank | NULL | Unknown Age |
解説:
CASE
: Searched CASEの開始です。評価対象の式は指定しません。WHEN age IS NULL THEN 'Unknown Age'
: 最初の条件はage
がNULL
かチェックします。これが真なら’Unknown Age’が返されます。NULL
の扱いはSearched CASEで明確に記述できます。WHEN age < 13 THEN 'Child'
: 次にage
が13未満かチェックします。この条件は、最初の条件(age IS NULL
)が偽だった場合にのみ評価されます。つまり、age
がNULLでなく、かつ13未満の場合に真となります。WHEN age < 20 THEN 'Teenager'
: 次にage
が20未満かチェックします。これは、それまでのどの条件も偽だった場合にのみ評価されます。つまり、age
がNULLでなく、13未満でもなく、かつ20未満の場合に真となります。結果的に、age
が13以上かつ20未満(13~19歳)の場合に’Teenager’が返されます。WHEN age < 60 THEN 'Adult'
: 同様に、age
が20以上かつ60未満(20~59歳)の場合に’Adult’が返されます。ELSE 'Senior'
: 上記のどの条件にも合致しなかった場合、つまりage
がNULLでなく、13未満でもなく、20未満でもなく、60未満でもない場合(60歳以上の場合)、’Senior’が返されます。END AS life_stage
: 結果にlife_stage
という別名をつけます。
Searched CASEでは、条件の評価順序が非常に重要です。条件は上から順に評価され、最初に真になった条件に対応する結果が採用されます。例えば、上記の例でWHEN age < 20 THEN 'Teenager'
をWHEN age < 13 THEN 'Child'
よりも前に記述した場合、10歳のユーザーはage < 20
が先に真になるため’Teenager’と分類されてしまいます。意図したロジックになるように、条件の順序を慎重に検討する必要があります。
複数の条件を組み合わせた例:
性別と年齢を組み合わせて割引率を計算するようなケースを考えます。
“`sql
— サンプルテーブル作成(ユーザーテーブルにgenderを追加)
CREATE TABLE users_with_gender (
id INTEGER PRIMARY KEY,
name TEXT NOT NULL,
age INTEGER,
gender TEXT — ‘Male’, ‘Female’, ‘Other’ など
);
— サンプルデータ挿入
INSERT INTO users_with_gender (name, age, gender) VALUES
(‘Alice’, 5, ‘Female’),
(‘Bob’, 15, ‘Male’),
(‘Charlie’, 30, ‘Male’),
(‘David’, 65, ‘Male’),
(‘Eve’, 80, ‘Female’),
(‘Frank’, NULL, ‘Male’),
(‘Grace’, 25, ‘Female’);
“`
このテーブルに対して、条件に応じた割引率を計算します。
sql
SELECT
name,
age,
gender,
CASE
WHEN age IS NULL OR gender IS NULL THEN 0.0 -- 年齢または性別不明は割引なし
WHEN age < 18 THEN 0.10 -- 18歳未満は10%割引
WHEN age >= 65 THEN 0.15 -- 65歳以上は15%割引
WHEN gender = 'Female' THEN 0.05 -- 18歳以上65歳未満の女性は5%割引
ELSE 0.0 -- それ以外(18歳以上65歳未満の男性など)は割引なし
END AS discount_rate
FROM users_with_gender;
実行結果:
name | age | gender | discount_rate |
---|---|---|---|
Alice | 5 | Female | 0.1 |
Bob | 15 | Male | 0.1 |
Charlie | 30 | Male | 0.0 |
David | 65 | Male | 0.15 |
Eve | 80 | Female | 0.15 |
Frank | NULL | Male | 0.0 |
Grace | 25 | Female | 0.05 |
解説:
WHEN age IS NULL OR gender IS NULL THEN 0.0
: 年齢または性別が不明な場合は最優先で割引率0%とします。WHEN age < 18 THEN 0.10
: 次に、18歳未満であれば割引率10%とします。この条件は、前の条件が偽だった場合に評価されます(つまり、年齢も性別も不明でなく、18歳未満の場合)。WHEN age >= 65 THEN 0.15
: 次に、65歳以上であれば割引率15%とします。この条件は、それまでの条件がすべて偽だった場合に評価されます(つまり、年齢も性別も不明でなく、18歳未満でもなく、かつ65歳以上の場合)。WHEN gender = 'Female' THEN 0.05
: 次に、性別が’Female’であれば割引率5%とします。この条件は、それまでの条件がすべて偽だった場合に評価されます(つまり、年齢も性別も不明でなく、18歳未満でもなく、65歳以上でもなく、かつ性別が’Female’の場合。結果的に、18歳以上65歳未満の女性の場合に真となります)。ELSE 0.0
: 上記のどの条件にも合致しなかった場合(つまり、年齢も性別も不明でなく、18歳未満でもなく、65歳以上でもなく、性別が’Female’でもない場合。結果的に、18歳以上65歳未満の男性などの場合)、割引率0%とします。
このように、Searched CASEは複数の条件や範囲指定、NULLチェックなどを柔軟に組み合わせて、複雑なビジネスロジックをSQLで表現することを可能にします。
CASE文の応用例
CASE文は、単にSELECT文でデータを整形するだけでなく、様々なSQL句や関数と組み合わせて、より高度なデータ操作を行うことができます。
1. 集計関数との組み合わせ (条件付き集計)
CASE文を集計関数(COUNT, SUM, AVG, MIN, MAXなど)の引数として使用することで、特定の条件を満たすデータだけを集計することができます。これは「条件付き集計 (Conditional Aggregation)」と呼ばれるテクニックです。
例:ユーザーのステータスごとの人数をカウントする。
“`sql
— サンプルテーブル作成
CREATE TABLE user_status (
id INTEGER PRIMARY KEY,
name TEXT NOT NULL,
status TEXT — ‘Active’, ‘Inactive’, ‘Pending’
);
— サンプルデータ挿入
INSERT INTO user_status (name, status) VALUES
(‘Alice’, ‘Active’),
(‘Bob’, ‘Inactive’),
(‘Charlie’, ‘Active’),
(‘David’, ‘Pending’),
(‘Eve’, ‘Inactive’),
(‘Frank’, ‘Active’),
(‘Grace’, NULL); — ステータス不明
“`
このテーブルに対して、ステータスごとの人数を集計します。
sql
SELECT
COUNT(CASE WHEN status = 'Active' THEN 1 END) AS active_users,
COUNT(CASE WHEN status = 'Inactive' THEN 1 END) AS inactive_users,
COUNT(CASE WHEN status = 'Pending' THEN 1 END) AS pending_users,
COUNT(CASE WHEN status IS NULL THEN 1 END) AS unknown_status_users,
COUNT(*) AS total_users
FROM user_status;
実行結果:
active_users | inactive_users | pending_users | unknown_status_users | total_users |
---|---|---|---|---|
3 | 2 | 1 | 1 | 7 |
解説:
COUNT(CASE WHEN status = 'Active' THEN 1 END)
:status
が’Active’の場合に1を返し、それ以外の場合はNULL
(ELSE
節がないため)を返します。COUNT()
関数はNULL
以外の値をカウントするため、結果として’Active’なユーザーの数だけがカウントされます。- 他のステータスについても同様です。
COUNT(CASE WHEN status IS NULL THEN 1 END)
: ステータスがNULLの場合を明示的にカウントできます。COUNT(*)
: テーブル全体の行数をカウントします。
この方法を使えば、GROUP BY
句を使わずに、複数の条件に基づいた集計結果を1行で取得できます。
例:商品のカテゴリごとの合計金額と、合計金額に対する各カテゴリの割合を計算する。
“`sql
— サンプルテーブル作成
CREATE TABLE orders (
order_id INTEGER PRIMARY KEY,
product_name TEXT NOT NULL,
category TEXT, — ‘Electronics’, ‘Accessory’, ‘Mobile Device’
amount REAL NOT NULL
);
— サンプルデータ挿入
INSERT INTO orders (product_name, category, amount) VALUES
(‘Laptop’, ‘Electronics’, 1200.00),
(‘Keyboard’, ‘Accessory’, 75.00),
(‘Mouse’, ‘Accessory’, 25.00),
(‘Monitor’, ‘Electronics’, 300.00),
(‘Tablet’, ‘Mobile Device’, 500.00),
(‘Speaker’, ‘Electronics’, 150.00),
(‘Webcam’, ‘Accessory’, 50.00),
(‘Charger’, NULL, 30.00); — カテゴリ不明
“`
このテーブルに対して、カテゴリごとの合計金額と割合を計算します。
sql
SELECT
SUM(CASE WHEN category = 'Electronics' THEN amount ELSE 0 END) AS electronics_total,
SUM(CASE WHEN category = 'Accessory' THEN amount ELSE 0 END) AS accessory_total,
SUM(CASE WHEN category = 'Mobile Device' THEN amount ELSE 0 END) AS mobile_device_total,
SUM(CASE WHEN category IS NULL THEN amount ELSE 0 END) AS unknown_category_total,
SUM(amount) AS total_amount,
SUM(CASE WHEN category = 'Electronics' THEN amount ELSE 0 END) * 100.0 / SUM(amount) AS electronics_percentage,
SUM(CASE WHEN category = 'Accessory' THEN amount ELSE 0 END) * 100.0 / SUM(amount) AS accessory_percentage,
SUM(CASE WHEN category = 'Mobile Device' THEN amount ELSE 0 END) * 100.0 / SUM(amount) AS mobile_device_percentage
FROM orders;
実行結果 (小数点以下の桁数は環境による):
electronics_total | accessory_total | mobile_device_total | unknown_category_total | total_amount | electronics_percentage | accessory_percentage | mobile_device_percentage |
---|---|---|---|---|---|---|---|
1650.0 | 150.0 | 500.0 | 30.0 | 2330.0 | 70.9871244635193 | 6.43776824034335 | 21.45922746781116 |
解説:
SUM(CASE WHEN category = 'Electronics' THEN amount ELSE 0 END)
:category
が’Electronics’の場合にamount
を、それ以外の場合(NULLを含む)は0を合計します。ELSE 0
を指定することで、条件に合わない行が合計値に影響しないようにします。COUNT
の場合はELSE NULL
(省略可)で問題ありませんでしたが、SUM
やAVG
の場合はELSE 0
とすることが多いです。- 割合の計算は、各カテゴリの合計を全体の合計で割って100を掛けています。SQLiteでは整数同士の割り算は整数になるため、どちらかの値を浮動小数点数(例:
100.0
)にする必要があります。
このように、CASE文と集計関数を組み合わせることで、強力なクロス集計や条件ごとの合計/平均などを簡単に行うことができます。
2. GROUP BY句での使用
CASE文の結果に基づいて行をグループ化することができます。
例:年齢層ごとにユーザーをグループ化する。
sql
-- usersテーブルを使用 (年齢層分類の例で使用したものと同じ)
-- CREATE TABLE users ... ; INSERT INTO users ... ; は省略
sql
SELECT
CASE
WHEN age IS NULL THEN 'Unknown Age'
WHEN age < 20 THEN 'Under 20'
WHEN age < 40 THEN '20-39'
WHEN age < 60 THEN '40-59'
ELSE '60+'
END AS age_group,
COUNT(*) AS user_count
FROM users
GROUP BY
CASE
WHEN age IS NULL THEN 'Unknown Age'
WHEN age < 20 THEN 'Under 20'
WHEN age < 40 THEN '20-39'
WHEN age < 60 THEN '40-59'
ELSE '60+'
END;
実行結果:
age_group | user_count |
---|---|
20-39 | 1 |
60+ | 2 |
Under 20 | 2 |
Unknown Age | 1 |
解説:
GROUP BY
句にSELECT
リストと同じCASE文を指定することで、CASE文が返す値(’Under 20′, ’20-39’など)ごとにグループ化が行われます。これにより、年齢層ごとのユーザー数を簡単に集計できます。
SQL標準では、GROUP BY
句でSELECT
リストの別名(age_group
)を使用できる場合とできない場合がありますが、SQLiteは別名の使用をサポートしています。ただし、可搬性を考慮すると、GROUP BY
句にもCASE文を直接記述する方が安全です。
3. ORDER BY句での使用
CASE文の結果に基づいて並べ替えの順序を制御することができます。特定の値をリストの最初に表示したい場合や、複数の条件で異なる並べ替え順を適用したい場合に便利です。
例:ステータスが’Pending’のユーザーをリストの先頭に表示し、それ以外は名前順に並べる。
sql
-- user_statusテーブルを使用
-- CREATE TABLE user_status ... ; INSERT INTO user_status ... ; は省略
sql
SELECT
name,
status
FROM user_status
ORDER BY
CASE
WHEN status = 'Pending' THEN 1 -- 'Pending'を最優先 (1番目に表示)
ELSE 2 -- 'Pending'以外は次に表示
END,
name ASC; -- 次に名前の昇順で並べる
実行結果:
name | status |
---|---|
David | Pending |
Alice | Active |
Bob | Inactive |
Charlie | Active |
Eve | Inactive |
Frank | Active |
Grace | NULL |
解説:
ORDER BY CASE WHEN status = 'Pending' THEN 1 ELSE 2 END
: このCASE文は、status
が’Pending’の場合は1、それ以外の場合は2を返します。ORDER BY
句はこの値に基づいて昇順(デフォルト)で並べ替えるため、1を返す行が2を返す行より前に来ます。つまり、’Pending’の行がリストの先頭に表示されます。, name ASC
: CASE文の結果が同じ行(この場合は2を返す行)の中では、name
カラムの昇順で並べ替えます。
このテクニックを使えば、「特定のアイテムをリストの最上位に固定表示する」といったUI要件などにも対応できます。
4. UPDATE文での使用
CASE文を使用して、条件によって更新する値を切り替えることができます。
例:商品の価格を、カテゴリに応じて異なる割合で値上げする。
sql
-- productsテーブルを使用 (category_codeがINTEGERの例で使用したものと同じ)
-- CREATE TABLE products ... ; INSERT INTO products ... ; は省略
sql
UPDATE products
SET price =
CASE
WHEN category_code = 1 THEN price * 1.10 -- Electronicsは10%値上げ
WHEN category_code = 2 THEN price * 1.05 -- Accessoryは5%値上げ
ELSE price -- その他は価格変更なし
END
WHERE category_code IN (1, 2); -- 値上げ対象のカテゴリに絞る (任意)
解説:
UPDATE products SET price = ...
:products
テーブルのprice
カラムを更新します。更新する値はCASE文の結果によって決定されます。CASE WHEN category_code = 1 THEN price * 1.10
:category_code
が1の行では、現在のprice
を10%増加させた値が新しいprice
となります。WHEN category_code = 2 THEN price * 1.05
:category_code
が2の行では、現在のprice
を5%増加させた値が新しいprice
となります。ELSE price
: どのWHEN
条件にも一致しない行(category_code
が3や99、NULLの行)では、price
は元の値のまま変更されません。WHERE category_code IN (1, 2)
: このWHERE
句は、更新対象の行をcategory_code
が1または2の行に絞り込んでいます。もしこのWHERE
句がない場合、テーブル全体の行に対してCASE文が評価され、ELSE
節に該当する行も更新(ただし値は変わらない)されることになります。更新対象を絞ることで、無関係な行の処理を防ぐことができます。
この例のように、CASE文は単一のUPDATE
文内で複数の条件に基づいた異なる更新処理を実現できます。
また、更新するカラム自体をCASE文で切り替えることも可能です(ただし、戻り値のデータ型に注意が必要です)。
sql
-- 例: 商品の在庫レベルが低い場合に在庫状態を示すフラグを更新
-- (ここでは単純化のため在庫レベルのカラムはないが、存在すると仮定)
UPDATE products
SET status_flag =
CASE
-- WHEN stock_level < 10 THEN 'Low Stock'
-- WHEN stock_level BETWEEN 10 AND 50 THEN 'Normal Stock'
-- ELSE 'High Stock'
END
WHERE ... ;
5. INSERT文での使用
CASE文を使用して、挿入する値を条件に基づいて決定することができます。
例:新規ユーザー登録時、年齢に基づいてデフォルトの権限レベルを決定する。
sql
-- サンプルテーブル作成
CREATE TABLE users_with_role (
id INTEGER PRIMARY KEY,
name TEXT NOT NULL,
age INTEGER,
role TEXT -- 'Guest', 'Member', 'Admin' など
);
“`sql
— 新規ユーザーを挿入
INSERT INTO users_with_role (name, age, role)
VALUES (
‘New User’,
22, — 新規ユーザーの年齢
CASE
WHEN 22 < 18 THEN ‘Guest’ — 18歳未満はGuest
WHEN 22 < 65 THEN ‘Member’ — 18歳以上65歳未満はMember
ELSE ‘Senior Member’ — 65歳以上はSenior Member
END — 挿入するroleの値をCASE文で決定
);
— 別の新規ユーザーを挿入
INSERT INTO users_with_role (name, age, role)
VALUES (
‘Young User’,
16,
CASE
WHEN 16 < 18 THEN ‘Guest’
WHEN 16 < 65 THEN ‘Member’
ELSE ‘Senior Member’
END
);
SELECT * FROM users_with_role;
“`
実行結果:
id | name | age | role |
---|---|---|---|
1 | New User | 22 | Member |
2 | Young User | 16 | Guest |
解説:
INSERT INTO ... VALUES (...)
の括弧内にCASE文を記述することで、挿入するカラムの値としてCASE文の評価結果を使用できます。この例では、年齢という入力値に基づいてrole
カラムに挿入する値を動的に決定しています。
6. WHERE句での使用 (限定的)
CASE文はWHERE句でも使用できますが、その用途は限定的です。WHERE句は条件式が真 (TRUE
) となる行を絞り込むために使用されます。CASE文は値を返す式なので、WHERE句で使用する場合は、CASE文の戻り値と何らかの値を比較する形になります。
例:年齢が60歳以上のユーザー、または年齢が不明なユーザーを選択する。
sql
-- usersテーブルを使用
-- CREATE TABLE users ... ; INSERT INTO users ... ; は省略
sql
SELECT name, age
FROM users
WHERE
CASE
WHEN age IS NULL THEN 1 -- ageがNULLなら1
WHEN age >= 60 THEN 1 -- ageが60以上なら1
ELSE 0 -- それ以外は0
END = 1; -- CASE文の結果が1である行を選択
実行結果:
name | age |
---|---|
David | 65 |
Eve | 80 |
Frank | NULL |
解説:
このCASE文は、age
がNULLか60以上であれば1を返し、そうでなければ0を返します。WHERE句でCASE ... END = 1
とすることで、CASE文が1を返した行、つまりage
がNULLか60以上の行が選択されます。
ただし、上記の例は通常、CASE文を使わずに次のように記述する方が一般的で、可読性もパフォーマンスも良いことが多いです。
sql
SELECT name, age
FROM users
WHERE age IS NULL OR age >= 60;
CASE文をWHERE句で使うのは、例えば複雑な計算や複数のカラムの組み合わせによってフィルタリング条件が動的に変わるような、非常に特殊なケースに限られることが多いです。多くの場合、WHERE句では直接条件式を記述する方がシンプルです。
7. ビュー定義での使用
CASE文はビューのカラム定義にも使用できます。これにより、ビューを参照する際に、事前にCASE文による加工が適用された結果を取得できます。
例:ユーザーテーブルに、年齢層を判定するage_group
カラムを持つビューを作成する。
sql
-- usersテーブルを使用
-- CREATE TABLE users ... ; INSERT INTO users ... ; は省略
sql
CREATE VIEW user_age_groups AS
SELECT
id,
name,
age,
CASE
WHEN age IS NULL THEN 'Unknown Age'
WHEN age < 20 THEN 'Under 20'
WHEN age < 40 THEN '20-39'
WHEN age < 60 THEN '40-59'
ELSE '60+'
END AS age_group
FROM users;
ビューを作成後、通常のテーブルのように参照できます。
sql
SELECT * FROM user_age_groups WHERE age_group = '60+';
実行結果:
id | name | age | age_group |
---|---|---|---|
4 | David | 65 | 60+ |
5 | Eve | 80 | 60+ |
解説:
ビューuser_age_groups
は、元のusers
テーブルのカラムに加えて、CASE文で計算されたage_group
カラムを持ちます。このビューを利用することで、年齢層の分類ロジックをSQLクエリから分離し、再利用可能な形で定義できます。
CASE文を使用する際の注意点
CASE文は非常に強力ですが、使用にあたってはいくつか注意点があります。
-
可読性:
非常に多数のWHEN
節を持つCASE文や、複雑な条件が連なるSearched CASE文は、読みにくくなる可能性があります。複雑すぎる場合は、複数のCASE文に分けたり、データの設計を見直したり、必要であればアプリケーション側で処理を行うことも検討しましょう。 -
データ型:
THEN
節およびELSE
節で返される値のデータ型は、互換性があるか、暗黙的に変換可能である必要があります。SQLiteはデータ型に比較的寛容ですが、予期しない型変換によるエラーや問題を防ぐため、結果として同じデータ型になるように意識することが推奨されます。例えば、数値と文字列を混在させたりすると、結果の型が不定になったり、エラーになったりする可能性があります。sql
-- 良くない例 (数値と文字列が混在)
CASE
WHEN condition1 THEN 123 -- 数値
WHEN condition2 THEN 'Error' -- 文字列
ELSE NULL -- NULL (SQLiteでは型なし)
ENDこのような場合、SQLiteは可能な限り互換性のある型に変換しようとしますが、結果が期待通りにならないことがあります。
-
NULLの扱い:
- シンプルCASEの
expression WHEN valueN
の形式では、expression
がNULL
の場合、どのWHEN valueN
条件とも一致しません。NULL = valueN
が真にならないためです。expression
がNULLの場合に何か特定の値を返したい場合は、ELSE
節で処理するか、Searched CASEでWHEN expression IS NULL THEN ...
のように明示的に記述する必要があります。 - Searched CASEの
WHEN conditionN THEN ...
の形式では、conditionN
がNULL
と評価された場合、その条件は真 (TRUE
) とみなされずスキップされます。WHEN column IS NULL THEN ...
のようにIS NULL
演算子を使って明示的にNULLをチェックする必要があります。
- シンプルCASEの
-
評価順序:
Searched CASEでは、WHEN
節は上から順に評価され、最初に真になった条件に対応する結果が使用されます。条件の順序によって結果が変わる可能性があるため、ロジックを正確に反映するように記述順序を検討することが重要です。より限定的な条件を先に記述することが多いです。 -
パフォーマンス:
ほとんどの場合、適切に使用されたCASE文がクエリのパフォーマンスに重大な影響を与えることは少ないですが、非常に複雑なCASE文や、CASE文の条件内でインデックスが効きにくい関数や演算を使用している場合は、パフォーマンスに影響を与える可能性があります。特に大きなデータセットを扱う場合は、実行計画(EXPLAIN QUERY PLAN)を確認して、CASE文がボトルネックになっていないか確認すると良いでしょう。
CASE文と他のSQL機能との比較
-
IF-THEN-ELSE
(手続き型SQL): プログラミング言語のようなIF-THEN-ELSE
構文は、多くのデータベースシステムに存在しますが、これらは主に手続き型SQL(ストアドプロシージャや関数など)の中で使用される制御フロー文です。一方、CASE文はSQLクエリ(SELECT, UPDATE, INSERTなど)の中で使用される式であり、値を返す点が異なります。CASE文は手続き型言語の条件分岐のように処理のフローを制御するものではなく、あくまで条件に応じた値を計算するためのものです。 -
COALESCE
,NULLIF
関数:COALESCE(expr1, expr2, ...)
: 引数のリストの中から最初に見つかった非NULL値を返します。主にNULL値を特定のデフォルト値や別のカラムの値で置き換えたい場合に使用します。COALESCE(column, 0)
は「columnがNULLなら0、そうでなければcolumnの値」という意味になり、これはCASE WHEN column IS NULL THEN 0 ELSE column END
というSearched CASE文と同じ結果になります。COALESCE
の方がNULLの置き換えには簡潔です。NULLIF(expr1, expr2)
:expr1
とexpr2
が等しい場合はNULL
を返し、等しくない場合はexpr1
を返します。例えば、ゼロ除算を防ぐために分母が0の場合はNULLにしたい場合などに使います。NULLIF(denominator, 0)
は「denominatorが0ならNULL、そうでなければdenominatorの値」となり、これはCASE WHEN denominator = 0 THEN NULL ELSE denominator END
というシンプルCASE文と同じ結果になります。NULLIF
の方が特定の等価比較に基づくNULL化には簡潔です。
これらの関数は、CASE文で記述できる特定の場合の省略記法と考えることができます。単純なNULL処理や等価比較によるNULL化にはこれらの関数を使う方が簡潔で分かりやすいですが、それ以外の複雑な条件分岐にはCASE文が不可欠です。
-
サブクエリやJOIN: 特定のデータ変換や分類は、サブクエリや別の変換用テーブルとのJOINによって実現することも可能です。例えば、カテゴリコードをカテゴリ名に変換するために、カテゴリマスタテーブルを作成し、元のテーブルとJOINするといった方法です。これはデータの正規化の観点からは望ましいアプローチですが、変換対象が少なく、マスタテーブル化するほどでもない場合や、クエリ内で一時的に分類したい場合などは、CASE文を使う方が手軽でクエリも簡潔になることがあります。
CASE文は、複雑な条件に基づいた値の生成や変換、集計、並べ替えなど、クエリ内で完結させたい柔軟な条件処理において、これらの他の機能では代替できない独自の役割を果たします。
まとめ
SQLiteにおけるCASE文は、SQLクエリ内で条件に基づいて値を返す強力な式です。シンプルCASE式は単一の値に基づく等価比較による分岐に、Searched CASE式はより複雑な条件式や範囲指定による分岐に適しています。どちらの形式もWHEN ... THEN ...
節を複数持ち、ELSE
節でそれ以外のケースを指定できます。
CASE文はSELECT
文でのデータ整形にとどまらず、COUNT
やSUM
といった集計関数と組み合わせて条件付き集計を行ったり、GROUP BY
句やORDER BY
句でデータのグループ化や並べ替え順序を制御したり、UPDATE
文やINSERT
文で条件に応じた値の更新や挿入を行ったりと、SQLiteの様々な機能と組み合わせて活用できます。
本記事で紹介した具体的な使用例を通じて、CASE文がどのようにデータベース操作の柔軟性と表現力を高めるかを理解いただけたかと思います。約5000語にわたる詳細な説明と多様な例を通して、CASE文の基本的な使い方から応用テクニックまでを網羅しました。
CASE文を使いこなすことは、より効率的で柔軟なSQLクエリを作成するために不可欠です。ぜひ、これらの知識を実際のSQLiteデータベース操作に活かしてみてください。最初はシンプルな例から始め、徐々に複雑な条件や応用パターンに挑戦していくと良いでしょう。