SQL IN: リスト条件で行を絞り込む

SQL IN 演算子は、値がリスト内のいずれかの要素と一致するかを判定します。既知の離散値セットで絞り込むときは、OR を長く連結する代わりに SQL WHERE IN 述語を使うと読みやすくなります。ステータスコード、カテゴリ名、地域 ID のように候補が事前に決まっている条件で有効です。

リスト一致のための SQL IN の例

出力:

ここに出力が表示されます...

出力:

+----------+-------------+
| name     | category    |
+----------+-------------+
| Eraser   | stationery  |
| Keyboard | electronics |
| Mouse    | electronics |
| Notebook | stationery  |
+----------+-------------+
4 row(s)

この例の仕組み

  1. CREATE TABLEINSERT で、3 つのカテゴリにまたがる 5 行の products テーブルを用意します。
  2. WHERE category IN ('electronics', 'stationery') は、category が列挙した値のどれかに一致する行を返します。論理的には category = 'electronics' OR category = 'stationery' と同じです。
  3. furniture の行はリストに含まれないため除外されます。
  4. ORDER BY name で出力順を固定します。

リテラルのリストを使う IN は、OR 比較を連結した条件と等価です。ただし値セットが増えるほど、OR を追加していくより IN リストに要素を足す方が保守しやすくなります。

SQL IN 演算子とは?

SQL IN 演算子は、OR で結合した複数の等価比較を短く書くための構文です。WHERE x IN (a, b, c)WHERE x = a OR x = b OR x = c と論理的に同じです。SQL の三値論理では、どの要素にも一致せず、かつリスト内に NULL がなければ IN は false です。NULL が含まれていて完全一致がない場合は結果が unknown になり、WHERE 句ではその行が除外されます。

SQL WHERE IN の空リストを扱う

多くのデータベースは WHERE col IN () を構文エラーとして拒否します。これは SQL WHERE IN リストを動的生成するときに起きやすい問題です。空リストは明示的に分岐し、フィルタ自体を省略するか、空なら 0 件を返すべきケースでは WHERE 1 = 0 のような常に偽の条件を使います。

SQL NOT IN と NULL の落とし穴

SQL NOT IN は判定を反転し、値がリスト内のすべての要素と異なるときだけ true になります。リストに NULL が 1 つでもあると、x <> NULL が常に unknown になるため、結果全体が unknown に変わることがあります。WHERE 句では unknown は false 扱いです。

-- 0 行を返す: NULL が述語を UNKNOWN にする
SELECT name
FROM products
WHERE category NOT IN ('electronics', NULL);

リストがサブクエリ由来の場合は、サブクエリ側で NULL を除外するか、NULL 安全な意味論を持つ NOT EXISTS を使います。

サブクエリと IN の組み合わせ

IN はリテラルリストだけでなくサブクエリも受け取れます。考え方としては、サブクエリが値集合を生成し、外側のクエリがその集合に含まれるかを判定します。

SELECT name FROM products
WHERE category IN (
  SELECT category FROM products WHERE name LIKE '%board%'
);

小さくて非相関のサブクエリなら、この形で十分に機能します。サブクエリが大量行を返す場合や相関がある場合は、最適化器がセミ結合戦略を使いやすい EXISTS 述語や JOIN の方が高性能になることがあります。

IN と EXISTS と JOIN の使い分け

アプローチ使う場面
IN (list)小さく既知のリテラル値セットで絞り込む
IN (subquery)小さく NULL を含まない結果集合への所属判定
EXISTS (subquery)相関サブクエリや NULL を取りうる外部キー
JOIN出力に両テーブルの列が必要
BETWEEN値が離散集合ではなく連続範囲を成す

原則: 離散値の集合には IN を使います。サブクエリが相関している、または NULL を含みうるなら EXISTS に切り替えます。両側の列が必要なら JOIN を使います。

パフォーマンスに関する注意点

インデックスがある列に対する IN は sargable で、最適化器はリスト内の各値に対してインデックス参照を使えます。エンジンによってはリテラル IN リストを内部でソートし、二分探索に近い評価を行います。非常に大きいリスト(数百件規模)は構文解析や実行計画生成を遅くするため、一時テーブルに値をロードして JOIN する方が安定します。

FAQ

SQL IN は OR 条件を複数書くのと同じですか?

論理的には同じです。WHERE x IN (1, 2, 3)WHERE x = 1 OR x = 2 OR x = 3 と等価です。最適化器が同じ実行計画を作ることも多いですが、リストが増えるほど IN の方が短く保守しやすくなります。

サブクエリに NULL があると、なぜ NOT IN は 0 行になりますか?

NOT INAND 連結に展開されます: x <> a AND x <> b AND x <> NULLx <> NULL は常に unknown なので式全体は true になりません。サブクエリ側で NULL を除外するか、NOT EXISTS を使って回避します。

IN はサブクエリと一緒に使えますか?

使えます。WHERE col IN (SELECT col FROM other_table) は、サブクエリ結果集合への所属判定です。小さく NULL を含まない結果集合では効率的です。結果集合が大きい、または NULL が混じる可能性がある場合は、EXISTS や JOIN の方が最適化器に柔軟性を与えやすくなります。