SQL IN: リスト条件で行を絞り込む
SQL IN 演算子は、値がリスト内のいずれかの要素と一致するかを判定します。既知の離散値セットで絞り込むときは、OR を長く連結する代わりに SQL WHERE IN 述語を使うと読みやすくなります。ステータスコード、カテゴリ名、地域 ID のように候補が事前に決まっている条件で有効です。
リスト一致のための SQL IN の例
出力:
ここに出力が表示されます...
出力:
+----------+-------------+
| name | category |
+----------+-------------+
| Eraser | stationery |
| Keyboard | electronics |
| Mouse | electronics |
| Notebook | stationery |
+----------+-------------+
4 row(s)
この例の仕組み
CREATE TABLEとINSERTで、3 つのカテゴリにまたがる 5 行のproductsテーブルを用意します。WHERE category IN ('electronics', 'stationery')は、categoryが列挙した値のどれかに一致する行を返します。論理的にはcategory = 'electronics' OR category = 'stationery'と同じです。furnitureの行はリストに含まれないため除外されます。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 IN は AND 連結に展開されます: x <> a AND x <> b AND x <> NULL。x <> NULL は常に unknown なので式全体は true になりません。サブクエリ側で NULL を除外するか、NOT EXISTS を使って回避します。
IN はサブクエリと一緒に使えますか?
使えます。WHERE col IN (SELECT col FROM other_table) は、サブクエリ結果集合への所属判定です。小さく NULL を含まない結果集合では効率的です。結果集合が大きい、または NULL が混じる可能性がある場合は、EXISTS や JOIN の方が最適化器に柔軟性を与えやすくなります。