【SQLite】CASE文の書き方と具体的な使用例


【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文には、主に以下の二つの形式があります。

  1. シンプルCASE式 (Simple CASE Expression)
    特定のカラムや式の値に基づいて分岐する場合に使用します。

    sql
    CASE expression
    WHEN value1 THEN result1
    [WHEN value2 THEN result2]
    [...]
    [ELSE result_else]
    END

    • CASE expression: 評価対象となるカラム名または式を指定します。
    • WHEN valueN: expressionの値と比較する値を指定します。
    • THEN resultN: expressionvalueNと一致した場合に返される値を指定します。
    • ELSE result_else: どのWHEN条件にも一致しなかった場合に返される値を指定します。ELSE節を省略した場合、どのWHEN条件にも一致しない場合はNULLが返されます。
    • END: CASE文の終わりを示します。必須です。
  2. Searched CASE式 (Searched CASE Expression)
    より複雑な条件や複数のカラムを組み合わせた条件に基づいて分岐する場合に使用します。シンプルCASEよりも柔軟性が高いです。

    sql
    CASE
    WHEN condition1 THEN result1
    [WHEN condition2 THEN result2]
    [...]
    [ELSE result_else]
    END

    • CASE: 式の開始を示します。シンプル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のような比較で行われるため、expressionNULLの場合、どのWHEN valueN条件とも一致しません(NULL = valueNは真にならないため)。

もし評価対象のexpressionNULLである場合に特定の値を返したい場合は、シンプルCASEではELSE節で処理するか、あるいはSearched CASEを使用する必要があります。

上記の例で、category_codeNULLの場合に特定の文字列(例: ‘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': 最初の条件はageNULLかチェックします。これが真なら’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を返し、それ以外の場合はNULLELSE節がないため)を返します。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(省略可)で問題ありませんでしたが、SUMAVGの場合は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文は非常に強力ですが、使用にあたってはいくつか注意点があります。

  1. 可読性:
    非常に多数のWHEN節を持つCASE文や、複雑な条件が連なるSearched CASE文は、読みにくくなる可能性があります。複雑すぎる場合は、複数のCASE文に分けたり、データの設計を見直したり、必要であればアプリケーション側で処理を行うことも検討しましょう。

  2. データ型:
    THEN節およびELSE節で返される値のデータ型は、互換性があるか、暗黙的に変換可能である必要があります。SQLiteはデータ型に比較的寛容ですが、予期しない型変換によるエラーや問題を防ぐため、結果として同じデータ型になるように意識することが推奨されます。例えば、数値と文字列を混在させたりすると、結果の型が不定になったり、エラーになったりする可能性があります。

    sql
    -- 良くない例 (数値と文字列が混在)
    CASE
    WHEN condition1 THEN 123 -- 数値
    WHEN condition2 THEN 'Error' -- 文字列
    ELSE NULL -- NULL (SQLiteでは型なし)
    END

    このような場合、SQLiteは可能な限り互換性のある型に変換しようとしますが、結果が期待通りにならないことがあります。

  3. NULLの扱い:

    • シンプルCASEのexpression WHEN valueNの形式では、expressionNULLの場合、どのWHEN valueN条件とも一致しません。NULL = valueNが真にならないためです。expressionがNULLの場合に何か特定の値を返したい場合は、ELSE節で処理するか、Searched CASEでWHEN expression IS NULL THEN ...のように明示的に記述する必要があります。
    • Searched CASEのWHEN conditionN THEN ...の形式では、conditionNNULLと評価された場合、その条件は真 (TRUE) とみなされずスキップされます。WHEN column IS NULL THEN ...のようにIS NULL演算子を使って明示的にNULLをチェックする必要があります。
  4. 評価順序:
    Searched CASEでは、WHEN節は上から順に評価され、最初に真になった条件に対応する結果が使用されます。条件の順序によって結果が変わる可能性があるため、ロジックを正確に反映するように記述順序を検討することが重要です。より限定的な条件を先に記述することが多いです。

  5. パフォーマンス:
    ほとんどの場合、適切に使用された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): expr1expr2が等しい場合は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文でのデータ整形にとどまらず、COUNTSUMといった集計関数と組み合わせて条件付き集計を行ったり、GROUP BY句やORDER BY句でデータのグループ化や並べ替え順序を制御したり、UPDATE文やINSERT文で条件に応じた値の更新や挿入を行ったりと、SQLiteの様々な機能と組み合わせて活用できます。

本記事で紹介した具体的な使用例を通じて、CASE文がどのようにデータベース操作の柔軟性と表現力を高めるかを理解いただけたかと思います。約5000語にわたる詳細な説明と多様な例を通して、CASE文の基本的な使い方から応用テクニックまでを網羅しました。

CASE文を使いこなすことは、より効率的で柔軟なSQLクエリを作成するために不可欠です。ぜひ、これらの知識を実際のSQLiteデータベース操作に活かしてみてください。最初はシンプルな例から始め、徐々に複雑な条件や応用パターンに挑戦していくと良いでしょう。


コメントする

メールアドレスが公開されることはありません。 が付いている欄は必須項目です

上部へスクロール