【SQL】指定した値に一致しないデータを抽出する方法|<>・NOT IN・NOT EXISTS・NOT LIKEの使い分け

SQLで指定した値に一致しないデータを抽出するには、<>(不等号)演算子を使います。さらに、複数の値を除外する NOT IN、サブクエリで除外する NOT EXISTS、パターン不一致の NOT LIKE など、目的に応じた複数の方法があります。

この記事では、「一致しないデータ」を抽出するための7つの方法を比較表付きで解説し、NULLの罠やパフォーマンスの違い、RDBMS別の注意点まで網羅します。

スポンサーリンク

サンプルテーブル

以下の employees テーブルを使って各方法を解説します。自分の環境で試す場合は下のSQLでテーブルを作成してください。

サンプルテーブルのCREATE文を表示
CREATE TABLE employees (
  id         INT PRIMARY KEY,
  name       VARCHAR(50),
  department VARCHAR(50),
  salary     INT,
  email      VARCHAR(100)
);

INSERT INTO employees VALUES
(1, '田中太郎', '営業', 350000, 'tanaka@example.com'),
(2, '鈴木花子', '開発', 450000, 'suzuki@example.com'),
(3, '佐藤一郎', '営業', 300000, 'sato@example.com'),
(4, '山田美咲', '人事', 380000, 'yamada@example.com'),
(5, '高橋健太', '開発', 500000, NULL),
(6, '伊藤直美', NULL,   NULL,   'ito@example.com');
id name department salary email
1 田中太郎 営業 350000 tanaka@example.com
2 鈴木花子 開発 450000 suzuki@example.com
3 佐藤一郎 営業 300000 sato@example.com
4 山田美咲 人事 380000 yamada@example.com
5 高橋健太 開発 500000 NULL
6 伊藤直美 NULL NULL ito@example.com

方法1: <> 演算子(不等号)

<> はSQL標準の「等しくない」演算子です。指定した1つの値に一致しないデータを抽出する最も基本的な方法です。

-- 営業部以外の社員を取得
SELECT * FROM employees
WHERE department <> '営業';

結果:

id name department salary
2鈴木花子開発450000
4山田美咲人事380000
5高橋健太開発500000

⚠️ NULLに注意

id=6(伊藤直美)は departmentNULL のため、結果に含まれません。NULLは <> で比較しても TRUE にならず、UNKNOWN(不明)と評価されるためです。NULLを含めたい場合は後述の対策が必要です。

<> と != の違い

<>!= はどちらも「等しくない」を意味しますが、微妙な違いがあります。

演算子 SQL標準 対応RDBMS 推奨度
<> 標準 全RDBMS ◎ 推奨
!= 非標準 MySQL / PostgreSQL / Oracle / SQL Server △ 動作するが非標準
-- どちらも同じ結果を返す
SELECT * FROM employees WHERE department <> '営業';
SELECT * FROM employees WHERE department != '営業';

-- 可搬性を考慮するなら <> を使うのが安全

方法2: NOT IN(複数の値を除外)

NOT IN複数の値に一致しないデータを抽出するときに使います。

-- 営業部と人事部以外の社員を取得
SELECT * FROM employees
WHERE department NOT IN ('営業', '人事');

結果: 鈴木花子(開発)、高橋健太(開発)の2件。伊藤直美(NULL)は含まれません。

⚠️ NOT IN と NULL の危険な組み合わせ

NOT IN のリストに NULL が含まれると、結果が0件になります。これはSQLで最もよくあるバグの一つです。

-- ❌ NOT IN に NULL が含まれると結果が0件になる
SELECT * FROM employees
WHERE department NOT IN ('営業', NULL);
-- 結果: 0件(全行がUNKNOWNと評価される)

-- 理由: NOT IN は内部的に AND で結合される
-- department <> '営業' AND department <> NULL
-- → NULLとの比較は常にUNKNOWN → 全体がUNKNOWN

-- ❌ サブクエリにNULLが混入するケースも危険
SELECT * FROM employees
WHERE department NOT IN (
  SELECT department FROM employees -- NULLが含まれる可能性あり
);
-- 結果: 0件になる可能性がある

-- ✅ 対策1: サブクエリでNULLを除外
SELECT * FROM employees
WHERE department NOT IN (
  SELECT department FROM employees
  WHERE department IS NOT NULL
);

-- ✅ 対策2: NOT EXISTS を使う(NULLに安全)
SELECT e1.* FROM employees e1
WHERE NOT EXISTS (
  SELECT 1 FROM other_table e2
  WHERE e1.department = e2.department
);

? Oracle の NOT IN 制限

Oracle では IN / NOT IN のリストに 1000件を超える値を指定できません。大量の値を除外する場合は一時テーブルと NOT EXISTS を使いましょう。詳しくは「IN句の1000件上限を回避する方法」を参照してください。

方法3: NOT EXISTS(サブクエリで除外)

NOT EXISTS は相関サブクエリと組み合わせて、「他のテーブルに対応するデータが存在しない」行を抽出します。NOT IN と違い、NULLに対して安全に動作します。

-- 注文が1件もない顧客を取得
SELECT * FROM customers c
WHERE NOT EXISTS (
  SELECT 1 FROM orders o
  WHERE o.customer_id = c.id
);

-- 特定の部署に所属していない社員を取得
SELECT * FROM employees e
WHERE NOT EXISTS (
  SELECT 1 FROM target_departments t
  WHERE t.dept_name = e.department
);

NOT IN と NOT EXISTS の使い分け

比較項目 NOT IN NOT EXISTS
NULL安全性 ✕ NULLで結果が0件になる ○ NULLに安全
パフォーマンス(大量データ) △ リスト全件を比較 ○ インデックスを活用しやすい
固定値リスト ○ 簡潔に書ける △ サブクエリが必要
サブクエリとの組み合わせ △ NULLに注意が必要 ◎ 推奨
可読性 ○ シンプル △ やや複雑

? 使い分けの目安

  • 固定値リスト(NULLなし確実)→ NOT IN が簡潔
  • サブクエリ(NULLの可能性あり)→ NOT EXISTS が安全
  • 大量データNOT EXISTS のほうがパフォーマンスが良い傾向

方法4: NOT LIKE(パターン不一致)

NOT LIKEパターンに一致しないデータを抽出します。ワイルドカード(%_)を使った部分一致の否定です。

-- メールアドレスが @example.com でない社員を取得
SELECT * FROM employees
WHERE email NOT LIKE '%@example.com';

-- 名前が「田」で始まらない社員を取得
SELECT * FROM employees
WHERE name NOT LIKE '田%';

-- 名前が3文字でない社員を取得(_ は任意の1文字)
SELECT * FROM employees
WHERE name NOT LIKE '___';

-- 複数のパターンを除外する場合は AND で結合
SELECT * FROM employees
WHERE email NOT LIKE '%@example.com'
  AND email NOT LIKE '%@test.com';

方法5: NOT BETWEEN(範囲外を抽出)

NOT BETWEEN は指定した範囲に含まれないデータを抽出します。数値・日付・文字列に使えます。

-- 給与が30万〜40万の範囲外の社員を取得
SELECT * FROM employees
WHERE salary NOT BETWEEN 300000 AND 400000;
-- 結果: 鈴木花子(450000)、高橋健太(500000)
-- ※ 伊藤直美(NULL)は含まれない

-- 上記は以下と同じ意味
SELECT * FROM employees
WHERE salary < 300000 OR salary > 400000;

-- 日付の範囲外を取得
SELECT * FROM orders
WHERE order_date NOT BETWEEN '2025-01-01' AND '2025-12-31';

方法6: IS NOT NULL(NULLでないデータ)

IS NOT NULL は値がNULLでないデータを抽出します。NULLの判定には必ず IS NOT NULL を使い、<> NULL は使ってはいけません。

-- メールアドレスが登録されている社員を取得
SELECT * FROM employees
WHERE email IS NOT NULL;
-- 結果: id=1,2,3,4,6 の5件

-- ❌ これは間違い(結果が常に0件になる)
SELECT * FROM employees
WHERE email <> NULL;
-- 結果: 0件(NULLとの比較は常にUNKNOWN)

-- 複数カラムのNULLチェックを組み合わせ
SELECT * FROM employees
WHERE department IS NOT NULL
  AND email IS NOT NULL;
-- 結果: id=1,2,3,4 の4件

方法7: EXCEPT / MINUS(差集合)

EXCEPT(Oracle では MINUS)は、2つのクエリの差集合を返します。「Aの結果からBの結果を除く」という形で除外条件を書けます。

-- MySQL以外(PostgreSQL, SQL Server, SQLite)
SELECT * FROM employees
EXCEPT
SELECT * FROM employees WHERE department = '営業';

-- Oracle の場合は MINUS を使用
SELECT * FROM employees
MINUS
SELECT * FROM employees WHERE department = '営業';
RDBMS EXCEPT MINUS EXCEPT ALL
MySQL (8.0.31+)
PostgreSQL
Oracle
SQL Server

NULLを含む「一致しない」データの完全な取得

SQLの三値論理(TRUE / FALSE / UNKNOWN)では、NULLとの比較は常に UNKNOWN になり、WHERE句でフィルタリングされます。NULLを含めて「一致しない」データを取得するには、明示的に IS NULL を組み合わせます。

-- ❌ NULLの行が取得できない
SELECT * FROM employees
WHERE department <> '営業';
-- 結果: 3件(伊藤直美が含まれない)

-- ✅ NULLも含めて取得する
SELECT * FROM employees
WHERE department <> '営業'
   OR department IS NULL;
-- 結果: 4件(伊藤直美も含まれる)

-- ✅ COALESCE を使う方法
SELECT * FROM employees
WHERE COALESCE(department, '') <> '営業';
-- NULLが空文字に変換されるため、全行が比較対象になる
-- ※ 空文字のdepartmentが存在する場合も取得されるので注意

-- ✅ MySQL限定: <=> 演算子(NULL安全等価)の否定
SELECT * FROM employees
WHERE NOT (department <=> '営業');
-- <=> はNULL同士も比較可能。NOT と組み合わせるとNULLの行も取得できる

NULLと各演算子の挙動まとめ

結果 説明
NULL <> '値' UNKNOWN WHERE句でフィルタされる
NULL NOT IN ('a', 'b') UNKNOWN WHERE句でフィルタされる
val NOT IN ('a', NULL) UNKNOWN 常に結果0件になる
NULL NOT LIKE '%値%' UNKNOWN WHERE句でフィルタされる
NULL NOT BETWEEN 1 AND 10 UNKNOWN WHERE句でフィルタされる
NULL IS NOT NULL FALSE IS NOT NULL だけがNULLを正しく判定できる

7つの方法の比較まとめ

方法 用途 NULL安全 構文例
<> 1つの値と不一致 col <> '値'
NOT IN 複数の値と不一致 col NOT IN ('a', 'b')
NOT EXISTS サブクエリで除外 NOT EXISTS (SELECT ...)
NOT LIKE パターン不一致 col NOT LIKE '%値%'
NOT BETWEEN 範囲外を抽出 col NOT BETWEEN 1 AND 10
IS NOT NULL NULL以外を抽出 col IS NOT NULL
EXCEPT / MINUS 差集合 SELECT ... EXCEPT SELECT ...

実務でよくあるパターン

複数条件の組み合わせ

-- 営業部以外 かつ 給与40万以上 かつ メールがNULLでない
SELECT * FROM employees
WHERE department <> '営業'
  AND salary >= 400000
  AND email IS NOT NULL;
-- 結果: 鈴木花子(開発, 450000)

-- 営業部でも人事部でもなく、給与が30万〜40万の範囲外
SELECT * FROM employees
WHERE department NOT IN ('営業', '人事')
  AND salary NOT BETWEEN 300000 AND 400000;

LEFT JOIN で一致しないデータを取得

NOT EXISTS の代替として、LEFT JOIN + IS NULL パターンも実務でよく使われます。

-- 注文がない顧客を取得(LEFT JOIN パターン)
SELECT c.* FROM customers c
LEFT JOIN orders o ON c.id = o.customer_id
WHERE o.customer_id IS NULL;

-- 上記と同じ結果(NOT EXISTS パターン)
SELECT * FROM customers c
WHERE NOT EXISTS (
  SELECT 1 FROM orders o
  WHERE o.customer_id = c.id
);

? LEFT JOIN vs NOT EXISTS

  • パフォーマンスはRDBMSやデータ量によって異なる
  • 可読性は NOT EXISTS のほうが意図が明確
  • MySQL では LEFT JOIN + IS NULL が速い傾向、PostgreSQL では NOT EXISTS が速い傾向

DELETE文での除外条件

-- 営業部以外の社員を削除
DELETE FROM employees
WHERE department <> '営業'
   OR department IS NULL;

-- 特定のIDリスト以外を削除
DELETE FROM employees
WHERE id NOT IN (1, 3, 5);

UPDATE文での除外条件

-- 営業部以外の社員に10%昇給
UPDATE employees
SET salary = salary * 1.1
WHERE department <> '営業'
  AND salary IS NOT NULL;

HAVING句での否定条件(グループ単位の除外)

-- 平均給与が40万未満の部署を除外して表示
SELECT department, AVG(salary) AS avg_salary, COUNT(*) AS cnt
FROM employees
WHERE department IS NOT NULL
GROUP BY department
HAVING AVG(salary) >= 400000;

-- 社員が1人しかいない部署を除外
SELECT department, COUNT(*) AS cnt
FROM employees
WHERE department IS NOT NULL
GROUP BY department
HAVING COUNT(*) <> 1;

パフォーマンスの注意点

状況 注意点 対策
<> でインデックスが効かない 不等号条件はフルスキャンになりやすい = 条件で対象を絞る、またはカバリングインデックスを検討
NOT IN のリストが大量 数百〜数千件のリストは非効率 一時テーブル + NOT EXISTS または LEFT JOIN
NOT LIKE '%値%' 前方一致でないLIKEはインデックスが効かない 全文検索インデックスを検討
NOT EXISTS サブクエリ 結合カラムにインデックスがないと遅い 結合カラムにインデックスを作成

よくあるエラーと注意点

問題 原因 解決策
NULLの行が取得できない <> はNULLを除外する OR col IS NULL を追加
NOT INの結果が0件になる リストにNULLが含まれている NULLを除外するか NOT EXISTS に書き換え
<> NULL で結果が0件 NULLは <> で比較できない IS NOT NULL を使う
!= が構文エラーになる 一部の古い環境では非対応 SQL標準の <> を使う
除外条件のクエリが遅い 不等号条件でインデックスが効かない EXPLAINで実行計画を確認し、インデックスを最適化

まとめ

SQLで「一致しないデータ」を抽出する方法を目的別に整理しました。最もよく使う <>NOT IN はNULLに注意が必要です。サブクエリで除外する場合は NOT EXISTS がNULLに安全でパフォーマンスも良い傾向にあるため、積極的に使いましょう。

関連記事