これまでの第1回『Window関数』、第2回『JOIN・サブクエリ』編で、データベーススペシャリスト試験のSQL問題に対応するための土台は固まりました。
最終回となる今回は、SQLの応用力と記述の質をもう一段階引き上げるための、珠玉のテクニックを集めた応用編です。
SELECT
句にロジックを埋め込むCASE
式、NULL
を自在に扱うCOALESCE
関数、行単位のきめ細やかな処理を実現するカーソル制御、そしてデータの品質を保証する制約定義まで。一つ一つは独立した知識ですが、知っているかどうかで解答の質と速度が大きく変わるものばかりです。
本記事で紹介するテクニックを武器に加え、SQLマスターへの最後のピースを埋めましょう。
目次
CASE
式とCOALESCE
関数│SQL内で条件分岐とNULL処理を実装
SQLでデータを取得するだけでなく、その値に応じて「もしAならばX、もしBならばY」といった動的な処理を加えたい場合があります。CASE
式は、SELECT
文などのSQL句の中に、そのような条件分岐ロジックを埋め込むための強力な構文です。
CASE
式:SQLのIF文
CASE
式には2つの書き方があります。
- 単純CASE式: 特定の列の値が何かによって処理を分ける(
switch
文に近い) - 検索CASE式: 複数の条件式によって処理を分ける(
if-elseif-else
文に近い)
【具体例】検索CASE式で給与レベルを判定する
従業員の給与(salary
)に応じて、「高」「中」「低」のランク付けを行う例です。
SELECT
employee_name,
salary,
CASE
WHEN salary >= 700 THEN '高'
WHEN salary >= 500 THEN '中'
ELSE '低'
END AS salary_rank
FROM
employees;
WHEN
句は上に書かれたものから順に評価されるため、条件の順序が重要です。
【頻出】GROUP BY
と組み合わせた条件付き集計
CASE
式が真価を発揮するのが、集計関数と組み合わせた条件付き集計です。これは、行を縦に集約しつつ、CASE
式で横方向の集計を行うイメージで、データのピボット(行列変換)を実現できます。
【具体例】部署ごとに性別の人員数を集計する
SELECT
dept_name,
-- 性別が男性の行だけをカウント
COUNT(CASE WHEN gender = '男性' THEN 1 END) AS male_count,
-- 性別が女性の行だけをカウント
COUNT(CASE WHEN gender = '女性' THEN 1 END) AS female_count
FROM
employees
GROUP BY
dept_name;
このクエリは、各部署について「男性の人数」と「女性の人数」を1行で表示する結果を返します。
COALESCE
関数:NULLをスマートに扱う
NULL
値は「値が存在しない」ことを示す特殊な状態であり、計算や比較で意図しない結果を招くことがあります。COALESCE
(コアレス)関数は、NULL
を別の意味のある値に置き換えるためのシンプルな関数です。
COALESCE
は引数を複数受け取り、その中で最初に見つかったNULL
でない値を返します。
【具体例】表示名をニックネーム、なければ本名、それもなければ「匿名」に
SELECT
COALESCE(nickname, real_name, '匿名') AS display_name
FROM
users;
このクエリは、nickname
列がNULL
でなければその値を、NULL
であれば次にreal_name
列の値を、それもNULL
であれば最後に `'匿名'` という文字列を返します。
COALESCE
は、CASE WHEN nickname IS NOT NULL THEN nickname ELSE ... END
という冗長な記述を簡潔にしてくれる、非常に便利な関数です。
カーソル制御の基本│アプリケーション連携と行単位操作
これまで解説してきたSQLは、条件に合致するデータの集合を一括で操作する「集合指向」の考え方に基づいています。しかし、業務アプリケーションの要件によっては、取得した結果を1行ずつ順番に処理したい場合があります。この「行単位」の操作を実現するのがカーソルです。
なぜカーソルが必要になるのか?
カーソルは主に、アプリケーションプログラム内(ストアドプロシージャや、Java/COBOLなどの言語に組み込まれたSQL)で利用されます。
例えば、「従業員レコードを1件ずつ読み込み、その内容に応じて外部のAPIを呼び出したり、複雑な計算を行って別のテーブルを更新したりする」といった処理は、一括のSQL文で記述するのが困難です。カーソルは、このような手続き的な処理を可能にします。
カーソルの基本フロー
カーソルの操作は、本を読む流れに似ています。
DECLARE
(宣言): 読みたい本(SELECT
文)を定義します。まだ本は開いていません。OPEN
(オープン): 本を開き、読み始める準備をします。この時点でSELECT
文が実行され、結果セットが内部的に作成されます。FETCH
(データ取得): 現在のページ(行)からデータを読み取り、変数に格納します。そして、次のページ(行)へと進みます。このFETCH
操作をループで繰り返します。CLOSE
(クローズ): 本を読み終えたら閉じます。これにより、データベースが確保していたリソースが解放されます。
【処理イメージ】
-- 1. DECLARE: emp_cursorという名前のカーソルを宣言
DECLARE emp_cursor CURSOR FOR
SELECT employee_id, salary FROM employees;
-- 2. OPEN: カーソルを開く
OPEN emp_cursor;
-- 3. FETCH: 最初の行を取得し、変数に格納
FETCH NEXT FROM emp_cursor INTO @id, @sal;
-- ループで全行を処理
WHILE (条件式) -- データが続く限り
BEGIN
-- ここで変数@id, @salを使った行単位の処理を実行
-- 次の行を取得
FETCH NEXT FROM emp_cursor INTO @id, @sal;
END;
-- 4. CLOSE: カーソルを閉じる
CLOSE emp_cursor;
DEALLOCATE emp_cursor; -- カーソルの定義を解放
WHERE CURRENT OF
: カーソル位置の行を操作
カーソルでFETCH
した行に対して、更新(UPDATE
)や削除(DELETE
)を行いたい場合、WHERE CURRENT OF カーソル名
という特別な句が使えます。これにより、「今読み取った、まさにその行」を安全かつ効率的に操作できます。
データベーススペシャリスト試験では、カーソルはパフォーマンス上の理由から多用すべきではないとされていますが、アプリケーションとの連携を想定した問題でその概念や一連の流れが問われるため、必ず押さえておきましょう。
データの整合性を守る制約定義│UNIQUEとCHECK
これまで解説してきたSQLの多くは、データそのものを操作するDML(Data Manipulation Language)でした。しかし、データベースの信頼性を担保するには、そもそも不正なデータが登録されないようにテーブルの構造自体にルールを設けるDDL(Data Definition Language)が不可欠です。
そのルールを定義するのが制約(Constraint)です。アプリケーション側でのチェックだけでなく、データベース自身がデータの整合性を守る最後の砦となります。
UNIQUE
制約:一意性を保証する
UNIQUE
制約は、指定された列(または列の組み合わせ)に重複する値が入ることを禁止します。主キー(PRIMARY KEY
)も一意性を保証しますが、UNIQUE
制約は主キー以外で一意にしたい列に対して使用します。
- 1つのテーブルに複数設定できる。
NULL
の扱いがデータベース製品によって異なる場合がある(一般的にNULL
は1つだけ許可されることが多い)。
【具体例】従業員テーブルのメールアドレスを重複させない
従業員番号(employee_id
)が主キーだとしても、連絡先となるメールアドレスも社内で一意であるべきです。
CREATE TABLE employees (
employee_id INT PRIMARY KEY,
employee_name VARCHAR(100),
email VARCHAR(255) UNIQUE -- email列にUNIQUE制約を設定
);
この設定により、すでに存在するメールアドレスを登録しようとすると、データベースがエラーを返して登録を防ぎます。
CHECK
制約:業務ルールを強制する
CHECK
制約は、列に挿入または更新される値が指定した条件を満たしているかを検証します。アプリケーションのバグなどによって不正なデータが登録されるのを防ぐ、非常に強力な機能です。
【具体例】給与は必ず0より大きい、販売終了日は開始日以降
CREATE TABLE products (
product_id INT PRIMARY KEY,
price INT CHECK (price > 0), -- 価格は必ず正の値
-- 販売終了日は、販売開始日以降でなければならない
start_date DATE,
end_date DATE,
CONSTRAINT date_check CHECK (end_date >= start_date)
);
CHECK (price > 0)
のように、その列自身に対するシンプルな条件を設定できます。CONSTRAINT date_check CHECK (end_date >= start_date)
のように、複数の列にまたがる複雑な条件を設定し、制約に名前を付けることも可能です。
これらの制約を適切に設計することで、データの品質と整合性が格段に向上し、システム全体の信頼性に繋がります。
知っていると便利な演算子と関数│CASTとBETWEEN
最後に、特定の状況でSQLの記述をよりシンプルかつ正確にしてくれる、便利な関数と演算子を2つ紹介します。これらは頻繁に使うものではありませんが、いざという時に役立つ知識です。
CAST
関数:明示的なデータ型変換
CAST
関数は、あるデータ型の値を、指定した別のデータ型に明示的に変換します。
例えば、VARCHAR
型(文字列)で格納されている数値を計算に使いたい場合や、TIMESTAMP
型(日時)から日付部分だけを取り出したい場合などに使用します。
【具体例】文字列として保存された日付を、日付型として扱う
sales_date
列が'20250923'
のようなVARCHAR(8)
で格納されているとします。このままでは日付としての比較ができません。
SELECT
order_id,
sales_amount
FROM
sales_log
WHERE
CAST(sales_date AS DATE) > '2025-09-01';
CAST(sales_date AS DATE)
によって、文字列が日付型に変換され、日付としての比較が可能になります。同様に、CAST(column AS INTEGER)
で数値型に変換して計算を行うことも一般的です。
BETWEEN
演算子:範囲指定を簡潔に
BETWEEN
演算子は、WHERE
句で値が特定の範囲内にあるかを判定するために使います。>=
と<=
を組み合わせた条件式を、よりシンプルに記述できます。
【重要】
BETWEEN
は境界値を含む(value1
以上、value2
以下)という点に注意してください。
【具体例】特定の期間に登録したユーザーを抽出する
entry_date
が2025-04-01
から2025-04-30
までのユーザーを抽出したい場合。
BETWEEN
を使わない場合
SELECT user_name, entry_date FROM users
WHERE entry_date >= '2025-04-01' AND entry_date <= '2025-04-30';
BETWEEN
を使った場合
SELECT user_name, entry_date FROM users
WHERE entry_date BETWEEN '2025-04-01' AND '2025-04-30';
このように、BETWEEN
を使うことで範囲指定の意図が明確になり、コードの可読性が向上します。数値や日付の範囲を指定する際には積極的に活用しましょう。
まとめ:SQLテクニックを習得し、信頼性の高いシステムを構築する
最終回となる今回は、SQLの応用力と記述の質を高める、実践的なテクニックを解説しました。個々の機能は独立していますが、いずれもプロフェッショナルなデータベース開発において重要な役割を果たします。
CASE
式とCOALESCE
関数:SELECT
句に条件分岐やNULL処理のロジックを組み込み、SQLの表現力を飛躍的に高めます。特に条件付き集計は強力です。- カーソル: 行単位の逐次処理を可能にし、SQLの集合指向処理だけでは対応できない、複雑なアプリケーションロジックを実現します。
- 制約 (
UNIQUE
,CHECK
): データベース自身にデータの整合性を保証させます。これにより、アプリケーションの不具合からデータを守り、システムの信頼性を根本から支えます。 - 便利関数・演算子:
CAST
による型変換やBETWEEN
による範囲指定は、コードの可読性を高め、意図を明確にする上で役立ちます。
これらのテクニックは、単なるSQLの知識に留まりません。データベーススペシャリストとして、いかにして堅牢で、保守性が高く、信頼できるシステムを構築するかという設計思想そのものに繋がっています。
全3回にわたり、データベーススペシャリスト試験で問われる高度なSQLテクニックを解説してきました。Window関数による分析、JOINやサブクエリによる結合、そして今回の応用技術。これらをマスターし組み合わせることで、あなたはどんな複雑な要件にも対応できるSQLの力を手に入れたはずです。
合格に向けて、最後の仕上げに励んでください。応援しています!