EXISTSとNOT EXISTS
関連する行の存在を効率的に確認します。
「EXISTSとNOT EXISTS」はCoddyKit上の無料SQL Academyレッスンです。 これはレッスン2/4です。 下記で完全なレッスンを無料で読むことができます。その後、ブラウザ内の組み込みコードエディタと24時間対応のAIチューターでハンズオン演習できます。 これはSQL Academy学習パスの一部であり、ウェブとCoddyKitアプリ全体で進捗が同期されます。 SQL Academyコースには全4レッスンが含まれています。
EXISTS とは
EXISTS 演算子は、サブクエリが 1 行以上を返すかどうかを確認します。サブクエリが何らかの結果を返す場合は TRUE に評価され、空の場合は FALSE に評価されます。
値を比較する他のサブクエリ演算子とは異なり、EXISTS が確認するのは存在だけです。サブクエリが返す実際のデータは確認しません。
サンプルテーブルの準備
EXISTS クエリを記述する前に、customers と orders という 2 つのテーブルを作成しましょう。このレッスンでは、これらのテーブルを使って EXISTS と NOT EXISTS の実際の動作を確認します。
CREATE TABLE customers (
customer_id INT PRIMARY KEY,
name VARCHAR(100),
country VARCHAR(50)
);
CREATE TABLE orders (
order_id INT PRIMARY KEY,
customer_id INT,
amount DECIMAL(10,2),
order_date DATE
);
INSERT INTO customers VALUES
(1, 'Alice', 'US'),
(2, 'Bob', 'UK'),
(3, 'Charlie', 'US'),
(4, 'Diana', 'DE');
INSERT INTO orders VALUES
(101, 1, 250.00, '2024-01-10'),
(102, 1, 180.00, '2024-02-15'),
(103, 2, 95.00, '2024-03-01'),
(104, 3, 430.00, '2024-03-22');EXISTS の基本構文
EXISTS の基本構文では、WHERE 句の内部に EXISTS を記述します。EXISTS 内のサブクエリは通常、外側のクエリの列を参照します。これは相関サブクエリと呼ばれます。
次のクエリは、少なくとも 1 件の注文を行ったすべての顧客を検索します。
SELECT customer_id, name
FROM customers c
WHERE EXISTS (
SELECT 1
FROM orders o
WHERE o.customer_id = c.customer_id
);EXISTS内のSELECT 1
サブクエリで実際の列を選択するのではなく、SELECT 1を使用していることに気づいたかもしれません。これは意図的なものです。EXISTSが確認するのは、行が存在するかどうかであり、その内容ではありません。
SELECT 1(またはSELECT *)を使用しても結果は変わりません。ただし、SELECT 1を使うと、実際の値が重要ではないことをデータベースエンジンと読み手の双方に明確に示せます。
-- Both of these return the same result
SELECT name FROM customers c
WHERE EXISTS (SELECT 1 FROM orders o WHERE o.customer_id = c.customer_id);
SELECT name FROM customers c
WHERE EXISTS (SELECT o.* FROM orders o WHERE o.customer_id = c.customer_id);データベースがEXISTSを評価する仕組み
外側のクエリの各行について、データベースは相関サブクエリを実行します。一致する行が1行見つかると、エンジンはスキャンを停止し、EXISTSをTRUEとして扱います。この短絡評価により、大きなテーブルでもEXISTSは非常に効率的に動作します。
これに対してJOINは、フィルタリングする前に一致する行の完全な集合を作成します。そのため、一致が存在するかどうかだけを知りたい場合は、処理が遅くなることがあります。
-- EXISTS short-circuits after first match
-- Efficient even when orders table has millions of rows
SELECT name, country
FROM customers c
WHERE EXISTS (
SELECT 1
FROM orders o
WHERE o.customer_id = c.customer_id
AND o.amount > 200
);NOT EXISTS:存在しない行を見つける
NOT EXISTSはその逆で、サブクエリが一致する行を見つけなかった場合にTRUEを返します。これは、「注文したことが一度もない顧客はどれか」のような問いに答える標準的なSQLの方法です。
通常のJOINやNOT INで解決しようとすると、NULLが関係する場合に誤った結果になることがあります。NOT EXISTSなら、この問題を完全に回避できます。
-- Customers who have NOT placed any order
SELECT customer_id, name
FROM customers c
WHERE NOT EXISTS (
SELECT 1
FROM orders o
WHERE o.customer_id = c.customer_id
);NULLがある場合のNOT EXISTSとNOT INの比較
NOT INに対するNOT EXISTSの重要な利点の1つは、NULLに安全なことです。NOT INで使用するサブクエリが1つでもNULLを返すと、NOT IN式全体がNULLになります。その結果、外側のクエリからは行が1行も返されません。
NOT EXISTSは値の等価性ではなく行の存在を評価するため、この問題の影響を受けません。
-- Dangerous: if any customer_id in orders is NULL,
-- NOT IN returns zero rows!
SELECT name FROM customers
WHERE customer_id NOT IN (SELECT customer_id FROM orders);
-- Safe: NOT EXISTS handles NULLs correctly
SELECT name FROM customers c
WHERE NOT EXISTS (
SELECT 1 FROM orders o
WHERE o.customer_id = c.customer_id
);複数の条件を指定したEXISTS
EXISTS内のサブクエリには、複数のWHERE条件を含め、任意の有効なSQLを記述できます。これにより、関連する行を非常に細かく条件指定して確認できます。たとえば、特定の月にしきい値を超える注文を行った顧客を探せます。
-- Customers who placed an order over 200 in March 2024
SELECT c.name, c.country
FROM customers c
WHERE EXISTS (
SELECT 1
FROM orders o
WHERE o.customer_id = c.customer_id
AND o.amount > 200
AND o.order_date >= '2024-03-01'
AND o.order_date < '2024-04-01'
);DELETEとUPDATEでのEXISTS
EXISTSはSELECT文だけで使用するものではありません。UPDATEやDELETEでも使用でき、別のテーブルに関連データが存在するかどうかに基づいて、行を変更または削除できます。
以下の例では、特定の国に属する顧客の注文を削除します。
-- Delete orders placed by US customers
DELETE FROM orders o
WHERE EXISTS (
SELECT 1
FROM customers c
WHERE c.customer_id = o.customer_id
AND c.country = 'US'
);EXISTSとJOINの比較
EXISTSとJOINでは同じ問いを表せることがよくありますが、動作は異なります。複数の一致がある場合、JOINは行を重複させます。一方、EXISTSは外側の各行を最大でも1回しか返しません。
関連テーブルのデータを取得するのではなく、関係が存在するかどうかだけを知りたい場合は、EXISTSのほうが簡潔で、通常は高速です。
-- JOIN may return duplicate customer rows if a customer has multiple orders
SELECT DISTINCT c.name
FROM customers c
JOIN orders o ON o.customer_id = c.customer_id;
-- EXISTS always returns each customer once
SELECT c.name
FROM customers c
WHERE EXISTS (
SELECT 1 FROM orders o
WHERE o.customer_id = c.customer_id
);データ品質チェックでのNOT EXISTS
NOT EXISTSは、データ品質の監査に役立つ強力なツールです。孤立したレコード、存在しない参照、または関連データが存在するはずなのに存在しない行を見つけるために使用できます。
以下のクエリでは、customer_idがcustomersテーブルのどの行とも一致しない注文行を検出します。これは、参照整合性が壊れている兆候です。
-- Find orders with no matching customer (orphaned records)
SELECT o.order_id, o.customer_id, o.amount
FROM orders o
WHERE NOT EXISTS (
SELECT 1
FROM customers c
WHERE c.customer_id = o.customer_id
);理解度チェック
EXISTSとNOT EXISTSについての理解度を確認しましょう。
レッスンのまとめ
このレッスンでは、EXISTSとNOT EXISTSを使うと、値を直接比較せずに関連する行の有無を確認できることを学びました。
主なポイント:
- EXISTSは、サブクエリが一致する行を1行見つけるとすぐにTRUEを返します(短絡評価)。
- NOT EXISTSは、サブクエリが一致する行を見つけなかった場合にTRUEを返します。
- EXISTS内では
SELECT 1を使用します。返される値は重要ではありません。 - NOT EXISTSはNULLに安全ですが、NOT INは安全ではありません。NULLが含まれる可能性がある場合は、NOT EXISTSを優先してください。
- EXISTSはSELECT、UPDATE、DELETE文で使用できます。
- 存在の確認だけが必要な場合、EXISTSはJOINよりも簡潔で高速なことがよくあります。
よくある質問
「EXISTSとNOT EXISTS」レッスンは無料ですか?
はい。「EXISTSとNOT EXISTS」の完全なテキストはこのウェブで無料で読めます。インタラクティブに演習し(組み込みコードエディタと24時間対応のAIチューター)、SQL Academyコースの残りをアンロックするには、CoddyKit PROにアップグレードしてください。 SQL Academyコースには全4レッスンが含まれています。
「EXISTSとNOT EXISTS」で何を学びますか?
関連する行の存在を効率的に確認します。 ブラウザで直接実行するハンズオンコードでSQL Academyを演習し、24時間対応のAIチューターがレッスンを進める中での質問に答えます。
SQL Academyを始めるのに経験は必要ですか?
事前経験は必要ありません。CoddyKitのSQL Academyは初級者から上級者向けに構成されているため、ここから始めるか最初から始めて、自分のペースで進むことができます。 これはレッスン2/4です。
「EXISTSとNOT EXISTS」レッスンにはどのくらい時間がかかりますか?
ほとんどのCoddyKitレッスンは約5~10分かかります。各レッスンはコンパクトでインタラクティブなので、着実に進歩し、ウェブとアプリ全体で正確に前回の場所から再開できます。
このSQL Academyレッスンでコードを書いて実行できますか?
はい。すべてのSQL Academyレッスンに組み込みコードエディタが含まれているため、ブラウザでリアルコードを書いて実行し、即座のAIフィードバックを取得できます。ローカル設定は不要です。
このコースのすべてのレッスン
- 相関サブクエリ
- EXISTSとNOT EXISTS
- IN、ANY、ALLの比較
- EXISTSとJOINのパフォーマンス