データベーススペシャリスト試験や応用情報技術者試験の午後問題で、必ずと言っていいほど登場するのが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
(最大値より小さい)といった使い分けが問われることがあります。それぞれの演算子がどのような意味になるか、具体例を元に整理しておきましょう。