SQLでデータベースから特定のデータを抽出する際、WHERE句は非常に重要な役割を果たします。

その中でもIN句は、複数の候補値の中からいずれかに一致するレコードを効率的に取得するための便利な構文です。

実務においては、単一の値を指定するだけでなく、大量のリストや他のクエリの結果を条件に指定する場面が多々あります。

この記事では、SQLのWHERE IN句の基本的な書き方から、パフォーマンスを考慮した実務的なテクニックまでを詳しく解説します。

WHERE IN句の基本構文と使い方

IN句は、指定したカラムの値が、括弧 () 内に記述されたリストのいずれかと一致するかどうかを判定するために使用します。

基本的な構文は、対象となるカラム名の後に IN を記述し、続けてカンマ区切りの値を指定する形をとります。

例えば、社員テーブルから特定の部署に所属する社員を取得する場合、以下のように記述します。

SQL
-- 営業部(Dept01)または開発部(Dept02)に所属する社員を抽出する
SELECT
    employee_id,
    employee_name,
    department_code
FROM
    employees
WHERE
    department_code IN ('Dept01', 'Dept02');
実行結果
employee_id | employee_name | department_code
------------+---------------+----------------
001         | 田中 太郎     | Dept01
003         | 佐藤 次郎     | Dept02
005         | 鈴木 花子     | Dept01

このように、複数の値を一括で条件指定できるのがIN句の最大の特徴です。

もしIN句を使わずに同じ結果を得ようとすると、比較演算子とOR演算子を組み合わせて記述しなければなりません。

IN句とOR演算子の違い

IN句は、論理的には複数のOR条件を繋げたものと同じ結果を返します。

しかし、可読性やメンテナンス性の観点から、多くの場合はIN句の使用が推奨されます。

以下のコードは、先ほどのIN句の例をOR演算子で書き換えたものです。

SQL
-- OR演算子を使用した書き換え
SELECT
    employee_id,
    employee_name
FROM
    employees
WHERE
    department_code = 'Dept01'
    OR department_code = 'Dept02';

条件が2つ程度であれば大きな差はありませんが、指定する値が10個、20個と増えた場合、OR演算子ではコードが非常に冗長になります。

IN句を使用することで、クエリの見通しが良くなり、記述ミスを防ぐことができます。

また、多くのデータベース管理システム (DBMS) では、IN句の方が内部的に最適化されやすいというメリットもあります。

特に値の数が多い場合、オプティマイザが効率的な検索アルゴリズムを選択しやすくなるため、パフォーマンスの向上が期待できます。

サブクエリを利用したIN句の活用

IN句の中には、静的な値のリストだけでなく、別のSELECT文の結果(サブクエリ)を指定することも可能です。

これにより、動的に変化するデータに基づいたフィルタリングが行えます。

例えば、「売上が100万円を超えたプロジェクトに所属している社員」を抽出したい場合、以下のように記述します。

SQL
-- 売上目標を達成したプロジェクトのメンバーを抽出
SELECT
    employee_name
FROM
    employees
WHERE
    project_id IN (
        SELECT
            project_id
        FROM
            projects
        WHERE
            sales_amount > 1000000
    );

このクエリでは、まず projects テーブルから条件に合う project_id をリストアップします。

その抽出されたIDリストを、外側のクエリのIN句が受け取り、社員の絞り込みを実行します。

実務ではテーブル間のリレーションを利用してデータを抽出することが多いため、このサブクエリを用いた手法は非常に頻繁に使用されます。

NOT IN句による除外条件の指定

特定のリストに含まれないデータを抽出したい場合には、NOT IN 句を使用します。

使い方はIN句と同じですが、指定した値のいずれにも一致しないレコードが取得対象となります。

SQL
-- 休職中(Status09)以外の社員をすべて抽出する
SELECT
    employee_name,
    status_code
FROM
    employees
WHERE
    status_code NOT IN ('Status09');

NOT IN句を利用する際は、NULL値の扱いに注意が必要です。

もしNOT IN句のリストの中に NULL が含まれている場合、クエリの結果は常に空(0件)になってしまいます。

これは、SQLにおけるNULLの比較結果が UNKNOWN になるという特性に起因します。

意図しない結果を避けるために、NOT IN句で指定するカラムやサブクエリの結果にはNULLが含まれないことを保証するか、IS NOT NULL条件を併用するようにしましょう。

複数カラムを対象とするIN句の応用

あまり知られていないかもしれませんが、多くのDBMSでは複数のカラムを組み合わせてIN句で判定することができます。

これは「行値構成子」と呼ばれる機能で、複合キーを持つテーブルからデータを抽出する際に非常に便利です。

SQL
-- 年(year)と月(month)の組み合わせが一致するデータを抽出
SELECT
    target_date,
    sales_value
FROM
    monthly_sales
WHERE
    (sales_year, sales_month) IN ((2025, 12), (2026, 1));

このように記述することで、年と月の特定のペアを一度に指定できます。

これをOR演算子で書くと (year = 2025 AND month = 12) OR (year = 2026 AND month = 1) となり、条件が増えるほど複雑になります。

複数カラムのIN句は、データの整合性を保ちながらスマートに記述できる実務的なテクニックです。

パフォーマンスを最適化するためのポイント

IN句を実務で使用する際、特に大規模なデータベースではパフォーマンスへの影響を考慮する必要があります。

まず、IN句に指定するカラムには、適切にインデックスが貼られていることを確認してください。

インデックスが存在しない場合、データベースはテーブル全体を走査(フルスキャン)することになり、検索速度が著しく低下します。

また、IN句に渡す値の数には、データベースごとに上限が設定されている場合があります。

例えば、Oracle DatabaseではIN句に指定できる値の数は1,000個までに制限されています。

数千件、数万件といった膨大なリストをIN句に渡す必要がある場合は、一時テーブルを作成してJOIN(結合)するか、EXISTS句の使用を検討してください。

IN句とEXISTS句の使い分け

サブクエリを利用する場合、IN句とEXISTS句のどちらを使うべきか迷うことがあります。

一般的に、サブクエリ側の結果セットが非常に大きい場合は、EXISTS句の方がパフォーマンスに優れる傾向があります。

EXISTS句は、条件に一致するレコードが1つでも見つかった時点でその行の判定を終了するため、効率的な処理が可能です。

逆に、IN句はサブクエリの結果を一度メモリ上にリスト化してから比較を行うため、リストが小さい場合には高速に動作します。

SQL
-- EXISTS句を使用した例
SELECT
    e.employee_name
FROM
    employees e
WHERE
    EXISTS (
        SELECT
            1
        FROM
            projects p
        WHERE
            p.project_id = e.project_id
            AND p.sales_amount > 1000000
    );

実務では、実行計画(Execution Plan)を確認しながら、最適な方法を選択することが求められます。

WHERE IN句の注意点とトラブルシューティング

IN句を使用する際に初心者が陥りやすいミスとして、データ型の不一致が挙げられます。

例えば、文字列型のカラムに対して数値をIN句のリストとして渡すと、暗黙の型変換が発生します。

暗黙の型変換が行われると、インデックスが使用されなくなるため、クエリの実行速度が急激に低下します。

必ずカラムの定義に合わせたデータ型を記述するように意識しましょう。

また、リスト内の重複した値は自動的に無視されますが、クエリの可読性を保つために重複は排除しておくのが無難です。

さらに、IN句の中にNULLを記述しても、NULL = NULL は真(True)にならないため、NULL値を持つレコードを取得することはできません。

NULLを条件に含めたい場合は、IN ('A', 'B') OR my_column IS NULL のように記述する必要があります。

まとめ

SQLのWHERE IN句は、複数の条件を簡潔に記述するための非常に強力なツールです。

OR演算子の繰り返しを避けることでコードの可読性が高まり、メンテナンス効率も向上します。

サブクエリを活用すれば、動的なデータ抽出も容易に行えるようになります。

一方で、NOT IN句でのNULLの扱いや、大量の値を指定した際のパフォーマンス低下など、実務レベルでは注意すべき点も存在します。

インデックスの適用や、必要に応じたEXISTS句への切り替えなど、データベースの特性を理解して使い分けることが重要です。

今回紹介した基本から応用までの知識を活かして、より効率的でクリーンなSQLクエリを作成してください。

適切な構文選択は、システムのパフォーマンスだけでなく、チーム全体の開発効率にも大きく貢献します。