SQLのWHERE条件で項目が「複数の値のどれか」を抽出したい場合に「 IN」を使う場合があります。SELECT *FROM テーブル名WHERE 項目名 IN ( 値リスト )みたいな。
他のテーブルの項目の値を副問合せを使って値リストを作成することもできます。
SELECT *FROM テーブル名WHERE 項目名 IN ( SELECT 項目名FROM テーブル名2WHERE 抽出条件 )
「IN」を使った抽出はRDBMSによってはインデックスが使われにくい傾向があるなどはありますが抽出結果はおおむね予想どおりのものです。
一方「複数の値以外のもの」を抽出したい場合に「NOT IN」を使う場合は注意が必要です。普通やる人は少ないですが
SELECT *FROM テーブル名WHERE 項目名 NOT IN ( 値リスト )
の値リストに「NULL」が入っているとこのSQLは結果を1行も返しません。これはSQLの「IN」が値リストのそれぞれに対し
項目名=値1 OR  項目名=値2 OR 項目名=値3、、、
と同じ意味として取り扱うのと同様
項目名<>値1 AND  項目名<>値2 AND 項目名<>値3、、、
と同義とみなされるためです。「IN」の場合は値リストに「NULL」が含まれていても項目の値がNULLのものを抽出しないだけですが「NOT IN」の場合「項目名 <> NULL」は項目の値が何であれ「UNKNOWN」を返します。「TRUE」になりません。そのためすべての行に対して条件が成立せず。結果は0行になります。
明示的に値リストに「NULL」を含める人はまずいませんが、副問合せが絡むと見逃してしまうリスクが存在します。
SELECT *FROM テーブル名WHERE 項目名 NOT IN ( SELECT 項目名FROM テーブル名2WHERE 抽出条件 )
としたとき、「テーブル名2」から選択した項目の値にNULLが入り込むことがありえます。これは入ることもあるが、「テーブル名2」の中のデータにもよることなので、テストなどでは問題なく動くのに実際に動かしてみると結果がまったくなくなったりすることが発生してしまう原因になるかもしれないということです。
NULLが入り込む可能性が捨てきれないなら「NOT EXISTS」で処理すべきだと思います。障害・事故につながるリスクは低いほうが望ましいです、