データベーススペシャリスト試験や応用情報技術者試験の午後問題で、必ずと言っていいほど登場するのがSQLのWHERE句です。単純な条件指定だけでなく、NULLの扱いやサブクエリ(副問い合わせ)と連携した複雑な検索条件を正しく読み解き、あるいは作成する能力が問われます。特に、LIKEによるパターンマッチング、INやEXISTSによる存在チェック、そしてSOMEやALLを使った数量比較は、つまずきやすいポイントの代表格と言えるでしょう。本記事では、これらのWHERE句で使われる主要な検索条件について、それぞれの役割と正しい使い方を網羅的に解説します。具体的なSQL例と実行結果、そして各演算子の使い分けの勘所を学び、得点力アップに繋げましょう。
目次
NULL値の判定 | =ではなくIS NULLを使う理由
WHERE句を学ぶ上で、最初の関門となるのがNULLの扱いです。NULLは「値が存在しない」ことを示す特殊な状態であり、0や空文字列('')とは全く異なります。そのため、カラム名 = NULLのような比較演算子を用いた判定は意図した結果を返しません。NULLかどうかを判定するには、専門の述語である IS NULL または IS NOT NULL を使用する必要があります。
なぜ=ではダメなのか?
SQLの世界では、NULLと他の値(NULL自身を含む)を比較した結果は、TRUEでもFALSEでもなく、「不明(UNKNOWN)」として扱われます。WHERE句は条件がTRUEになる行だけを抽出するため、評価結果がUNKNOWNになる行は結果セットに含まれません。
例えば、「bonus(ボーナス額)が登録されていない社員」を抽出したいと考えます。
【テーブル例:employees】
| id | name | bonus |
|---|---|---|
| 1 | 佐藤 | 100000 |
| 2 | 鈴木 | NULL |
| 3 | 高橋 | 0 |
このテーブルに対して、間違ったSQLと正しいSQLを実行してみましょう。
【間違いSQL】bonus = NULL
SELECT * FROM employees WHERE bonus = NULL;
【実行結果】
(0件)
bonusがNULLである鈴木さんのレコードが抽出されません。NULL = NULLの評価がUNKNOWNになるためです。
【正しいSQL】IS NULL
SELECT * FROM employees WHERE bonus IS NULL;
【実行結果】
| id | name | bonus |
|---|---|---|
| 2 | 鈴木 | NULL |
このように、IS NULLを使うことでNULL値を持つ行を正しく抽出できます。逆に、NULLでない行を抽出する場合はIS NOT NULLを使用します。データベーススペシャリスト試験などでは、このNULLの特性を突いた問題が頻出するため、「= NULLは使わない」と機械的に覚えてしまいましょう。
LIKE演算子によるあいまい検索│ %と_の使い分け
WHERE句で文字列の一部が一致するレコードを検索したい場合、LIKE演算子を使用します。完全一致を調べる=とは異なり、ワイルドカードと呼ばれる特殊な記号を使って「あいまい検索(部分一致検索)」を実現できるのが特徴です。LIKE演算子で使われる主なワイルドカードは%と_の2つです。
%(パーセント): 0文字以上の任意の文字列を表します。_(アンダースコア): 任意の一文字を表します。
これらのワイルドカードを組み合わせることで、様々なパターンの文字列検索が可能になります。
【テーブル例:products】
| product_id | product_name |
|---|---|
| A-001 | 応援タオル |
| A-002 | 復刻版タオル |
| B-001 | スポーツドリンク |
| B-002 | スポーツウェア |
具体的な使用例
1. 前方一致:「応援」から始まる商品
SELECT * FROM products WHERE product_name LIKE '応援%';
【実行結果】
| product_id | product_name |
|---|---|
| A-001 | 応援タオル |
2. 後方一致:「タオル」で終わる商品
SELECT * FROM products WHERE product_name LIKE '%タオル';
【実行結果】
| product_id | product_name |
|---|---|
| A-001 | 応援タオル |
| A-002 | 復刻版タオル |
3. 中間一致:「スポーツ」を含む商品
SELECT * FROM products WHERE product_name LIKE '%スポーツ%';
【実行結果】
| product_id | product_name |
|---|---|
| B-001 | スポーツドリンク |
| B-002 | スポーツウェア |
4. _を使った文字数指定
LIKE 'スポーツ__' は「スポーツ」+ 2文字の計7文字の文字列にマッチします。
SELECT * FROM products WHERE product_name LIKE 'スポーツ__';
【実行結果】
(0件)
-- 「スポーツドリンク」「スポーツウェア」は文字数が合わないためヒットしません。もし product_name に「スポーツ茶」というデータがあれば、LIKE 'スポーツ_' でヒットします。
%や_自体を検索したい場合
%や_という文字そのものを検索したい場合は、ESCAPE句を使ってエスケープ文字を定義します。例えば、\をエスケープ文字に指定すると、\%で%という文字、\_で_という文字を検索できます。
-- '割引率:10%'という文字列を検索する例
SELECT * FROM campaigns WHERE campaign_name LIKE '%10\%%' ESCAPE '\';
BETWEENとINによる範囲検査│午後試験での頻出パターン
数値や日付、あるいは複数の値を対象とした範囲を指定する際には、BETWEEN演算子とIN演算子が非常に役立ちます。これらは>=や<=、ORを複数組み合わせるよりも、SQLを簡潔かつ直感的に記述できるため、午後試験の長文SQLを読解する上で必須の知識です。
BETWEEN:連続的な範囲の指定
BETWEENは、指定した2つの値の間(両端の値を含む)にあるデータを抽出します。数値や日付などの連続したデータの範囲指定に用いられます。
カラム名 BETWEEN 値1 AND 値2 は カラム名 >= 値1 AND カラム名 <= 値2 と等価です。
【テーブル例:test_scores】
| student_id | score |
|---|---|
| S001 | 85 |
| S002 | 60 |
| S003 | 92 |
| S004 | 75 |
使用例:点数が75点から90点の間の生徒を抽出
SELECT * FROM test_scores WHERE score BETWEEN 75 AND 90;
【実行結果】
| student_id | score |
|---|---|
| S001 | 85 |
| S004 | 75 |
BETWEENは境界値を含む(以上・以下)という点をしっかり覚えておきましょう。
IN:離散的な値のリスト指定
INは、括弧 () で囲まれたリストの中に含まれる値と一致するデータを抽出します。ORを何度も書く必要がなくなり、コードの可読性が大幅に向上します。
カラム名 IN (値1, 値2, ...) は カラム名 = 値1 OR カラム名 = 値2 OR ... と等価です。
【テーブル例:employees】
| id | name | department_id |
|---|---|---|
| 1 | 佐藤 | D01 |
| 2 | 鈴木 | D03 |
| 3 | 高橋 | D02 |
| 4 | 田中 | D01 |
使用例:部署IDがD01(人事)またはD03(営業)の社員を抽出
SELECT * FROM employees WHERE department_id IN ('D01', 'D03');
【実行結果】
| id | name | department_id |
|---|---|---|
| 1 | 佐藤 | D01 |
| 2 | 鈴木 | D03 |
| 4 | 田中 | D01 |
INのリストには、直接値を記述するだけでなく、サブクエリ(副問い合わせ)を指定することも可能です。これは非常に強力で、応用情報やDBスペシャリスト試験では頻出のパターンです。
サブクエリ使用例:東京支社に所属する社員を抽出
departmentsテーブルからlocationが'東京'である部署IDのリストを取得し、そのリストに所属する社員をemployeesテーブルから検索します。
SELECT * FROM employees
WHERE department_id IN (SELECT department_id FROM departments WHERE location = '東京');
JOINの結合条件をWHERE句に書く際の注意点│内部結合と外部結合
JOINは複数のテーブルを特定のカラム(キー)を使って結合するための句です。最近のSQLでは、結合条件をFROM句内のONで指定するのが一般的ですが、古い記述法や一部のデータベースではWHERE句に結合条件を記述することもあります。この違いと、特に外部結合(OUTER JOIN)での注意点を理解しておくことは、レガシーシステムのSQL読解などで役立ちます。
ANSI SQL-92形式(ON句を使用) vs SQL-89形式(WHERE句を使用)
employees(社員)テーブルとdepartments(部署)テーブルをdepartment_idで結合する場合を考えます。
【推奨されるSQL-92形式】
SELECT e.name, d.department_name
FROM employees e
JOIN departments d ON e.department_id = d.department_id;
この形式では、JOINで結合するテーブルを明示し、ONで結合条件、WHEREで絞り込み条件と、役割が明確に分かれています。
【古いSQL-89形式】
SELECT e.name, d.department_name
FROM employees e, departments d
WHERE e.department_id = d.department_id;
FROM句でテーブルをカンマ区切りで列挙し、WHERE句に結合条件を記述します。両者に紐づくレコードのみが抽出されるため、結果として内部結合(INNER JOIN)と同じになります。
WHERE句での外部結合と特有の記号 (+)
問題は外部結合です。LEFT JOINやRIGHT JOINをWHERE句で表現する場合、Oracleなど一部のデータベースでは(+)という独自の演算子を使います。
LEFT JOINの例:部署に所属していない社員も含めて一覧表示
【SQL-92形式のLEFT JOIN`】
SELECT e.name, d.department_name
FROM employees e
LEFT JOIN departments d ON e.department_id = d.department_id;
社員テーブルを主軸(左側)とし、部署名が存在しない社員(例えばdepartment_idがNULLの社員)も結果に含めます。
【Oracleの(+)を使ったWHERE`句での表現】
SELECT e.name, d.department_name
FROM employees e, departments d
WHERE e.department_id = d.department_id (+);
(+)は、情報が不足している側(この場合はdepartmentsテーブル側)の結合カラムに付けます。「e.department_idに対応するd.department_idが無くても、eの行は表示してね」という意味合いになります。
この(+)記号は、どちらのテーブルが主軸なのかを瞬時に判断する必要があり、読解が難しいです。情報処理技術者試験でこの形式が出題されることは稀ですが、実務で遭遇する可能性はゼロではありません。原則としてON句を使うべきですが、知識として知っておくと良いでしょう。重要なのは、WHERE句に書かれた=`が、事実上の内部結合として機能している場合がある、という点です。
EXISTSによる存在チェック│INとの違いと相関サブクエリ
EXISTSは、サブクエリ(副問い合わせ)が1件でも結果を返せばTRUE、1件も返さなければFALSEを返す演算子です。特定の条件を満たすデータが「存在するかどうか」だけを判定したい場合に使用します。INがサブクエリの結果リストと「値」を比較するのに対し、EXISTSは結果の「有無」だけを見る、という点が根本的な違いです。
EXISTSは通常、相関サブクエリと組み合わせて使われます。相関サブクエリとは、外側のクエリ(主クエリ)のカラムを内側のクエリ(サブクエリ)のWHERE句で参照するような、相互に関連したサブクエリのことです。
EXISTSの動作イメージ
- 主クエリの行が1行ずつ処理される。
- その行の値を使い、サブクエリが実行される。
- サブクエリが1件でも結果を返せば、その主クエリの行は
TRUEと判定され、結果セットに含まれる。 - 主クエリの全行に対して、1〜3を繰り返す。
使用例:一度でも商品を購入したことがある顧客を一覧表示する
customers(顧客)テーブルとorders(注文)テーブルを使います。
【テーブル例:customers】
| customer_id | name |
|---|---|
| C01 | 斎藤 |
| C02 | 中村 |
| C03 | 吉田 |
【テーブル例:orders】
| order_id | customer_id | order_date |
|---|---|---|
| 1 | C01 | 2025-08-01 |
| 2 | C03 | 2025-08-02 |
| 3 | C01 | 2025-08-05 |
SELECT c.customer_id, c.name
FROM customers c
WHERE EXISTS (
SELECT *
FROM orders o
WHERE o.customer_id = c.customer_id -- 主クエリのc.customer_idを参照(相関サブクエリ)
);
【実行結果】
| customer_id | name |
|---|---|
| C01 | 斎藤 |
| C03 | 吉田 |
このSQLは、customersテーブルの各行(斎藤さん、中村さん、吉田さん)について、ordersテーブル内に対応するcustomer_idを持つ注文記録が存在するかを調べています。中村さん(C02)は注文記録がないため、サブクエリが0件となり、EXISTSがFALSEを返すので結果から除外されます。
IN vs EXISTS の使い分け
| 観点 | IN |
EXISTS |
|---|---|---|
| 比較対象 | 値のリスト | 結果セットの有無 |
| 得意な処理 | サブクエリが返す結果セットが小さい場合 | サブクエリが返す結果セットが大きい場合 |
| サブクエリ | 非相関サブクエリで使われることが多い | 相関サブクエリで使われることが多い |
| NULLの扱い | サブクエリ結果にNULLが含まれると意図しない結果になることがある(特にNOT IN) |
NULLの影響を受けにくい |
一般的に、EXISTSの方が高速に動作するケースが多いとされています。INはサブクエリを一度だけ実行し、その結果リストをメモリに保持して主クエリと比較しますが、EXISTSはサブクエリが1件でも見つかった時点で評価を打ち切るためです。DBスペシャリスト試験では、このパフォーマンスの違いを意識した選択が求められることがあります。
SOMEとALLによる数量比較│ANYとの関係とNOTの挙動
SOMEとALLは、INやEXISTSと同様にサブクエリと組み合わせて使う比較演算子です。サブクエリが返す複数の結果値と、主クエリの値を比較します。
SOME: サブクエリが返す結果のいずれか1つでも条件を満たせばTRUEになります。ALL: サブクエリが返すすべての結果に対して条件を満たせばTRUEになります。
なお、ANYはSOME`と全く同じ意味を持つシノニム(同義語)です。SQLの標準規格ではSOMEが採用されていますが、ANYも広く使われています。
【テーブル例:products】
| name | category | price |
|---|---|---|
| A | 文房具 | 100 |
| B | 文房具 | 150 |
| C | 食品 | 200 |
| D | 食品 | 300 |
SOME (ANY) の使用例
「文房具カテゴリのいずれかの商品よりも価格が高い商品」を抽出します。
サブクエリ (SELECT price FROM products WHERE category = '文房具') は (100, 150) という結果を返します。
SELECT *
FROM products
WHERE price > SOME (SELECT price FROM products WHERE category = '文房具');
-- WHERE price > ANY (...) と同じ
この条件は price > 100 OR price > 150 と展開されます。price > 100 を満たせば良いため、100より大きいすべての商品が対象です。
【実行結果】
| name | category | price |
|---|---|---|
| B | 文房具 | 150 |
| C | 食品 | 200 |
| D | 食品 | 300 |
ALLの使用例
「文房具カテゴリのすべての商品よりも価格が高い商品」を抽出します。
SELECT *
FROM products
WHERE price > ALL (SELECT price FROM products WHERE category = '文房具');
この条件は price > 100 AND price > 150 と展開されます。両方を満たす必要があるため、price > 150 が条件となります。
【実行結果】
| name | category | price |
|---|---|---|
| C | 食品 | 200 |
| D | 食品 | 300 |
IN, =, <> との関係
これらの演算子は、他の演算子と密接な関係にあります。
= SOME (リスト)はIN (リスト)` と全く同じです。「リストのいずれかと等しい」という意味になります。<> ALL (リスト)はNOT IN (リスト)` とほぼ同じです。「リストのすべてと等しくない」という意味です。(ただし、NOT INはリスト内にNULLがあると結果が0件になるという罠がありますが、<> ALLはNULLを無視する挙動を示すDBMSもあり、厳密には挙動が異なる場合があります。)<> SOME (リスト)は「リストのいずれかと等しくない」という意味です。例えばリストが(100, 150)の場合、100でないか、または150でない、という条件なので、ほとんどのデータがTRUEになり、実用的な使い方は稀です。
情報処理技術者試験では、> ALL(最大値より大きい)、< ALL(最小値より小さい)、> SOME(最小値より大きい)、< SOME(最大値より小さい)といった使い分けが問われることがあります。それぞれの演算子がどのような意味になるか、具体例を元に整理しておきましょう。