【SQL】EXISTSとは?NOT EXISTS・INとの違いや使い方を実例で解説

  • 2026.10.08
       
【SQL】EXISTSの使い方をサンプルコードで解説

SQLのEXISTSは、別のテーブルに条件に合うデータが「あるかどうか」でレコードを絞り込むための句です。本記事では、基本の書き方を実行結果付きで解説したうえで、NOT EXISTSやIN・JOINとの違い、UPDATE・DELETE・CASE式での使い方まで紹介します。

<この記事の要点>

  • EXISTSは、条件に合うデータが別のテーブルに「あるかどうか」を調べるための句
  • サブクエリが1行でも返せば「TRUE」、1行も返さなければ「FALSE」になる
  • 反対に「ないデータ」を探したいときはNOT EXISTSを使う

EXISTS句の概要とサブクエリの基礎知識

SQLは、データベースからデータを取り出したり、追加・更新・削除したりするための言語です。SQLの命令文(クエリ)は、SELECTなどの「文」に、WHEREなどの「句」を組み合わせて書きます。EXISTSも句の1つなので単独では使えず、SELECT文などの中で「サブクエリ」と一緒に使います。

EXISTSとは?

SQLの「EXISTS」とは、指定された条件にあてはまるレコードが存在するか否かを調べるのに使用される句です。
EXISTS句は必ずサブクエリと併用され、サブクエリで1つ以上あてはまるレコードが存在した場合は「TRUE」を返し、そうでない場合は「FALSE」を返します。

データベーステーブルのカラムに対して複雑な条件を付けてレコードを取り出す場合、それぞれの条件に合わせてSELECT文を複数回使用するという方法をとることも考えられるかもしれません。
ですが、膨大な量のテーブルデータのなかから特定のデータを出力するために何度も同じような処理を繰り返すのは効率が悪く、データベースの処理も重くなるので、良いアプローチではありません。

そんなとき、EXISTS句を使用して条件を細かく絞り込めば、必要な情報を効率的に取り出すことができます。

なお、条件付きで出力を行う場合はIN句やJOIN句などを使うことも可能ですが、EXISTSを使えばより簡単に条件付きのSELECT文を記述できます。

【使用イメージ】

select * from テーブルA  -- 外側のSQL(メインクエリ)
where exists (
	select * from テーブルB  -- EXISTS内のSQL(サブクエリ)
	where テーブルB.列 = テーブルA.列  -- 外側のテーブルと紐づける条件
)

「–」から行末まではコメントで、SQLとしては実行されません。MySQLでは、MySQL公式リファレンスのコメント構文にあるとおり、「–」の後ろに半角スペースが必要です。ほかの言語の感覚で「//」と書くと構文エラーになるので注意しましょう。

サブクエリの中に「テーブルB.列 = テーブルA.列」のような紐づけの条件を書き、テーブルAの行ごとに「対応する行がテーブルBにあるか」を調べるのが基本の形です。

EXISTS句は、特定の条件にあてはまるレコードが存在するか否かを調べるのに使用されます。そのため、サブクエリで具体的な値を返す必要はなく、少なくとも1行のレコードがあてはまるかどうかで「TRUE」または「FALSE」を返すだけです。

そのため、サブクエリのSELECT文では、具体的な値ではなく、単に「SELECT *」や「SELECT 1」と書くのが一般的です。

サブクエリとは?

「サブクエリ」とは、メインクエリ内にネスト(入れ子)されたクエリのことです。SQL文の一部として内部で実行され、その実行結果をメインクエリに提供します。SELECT文を使用する際によく用いられ、日本語では「副問い合わせ」とも呼ばれます。条件を指定したデータの出力を行うには「サブクエリ」が有効です。たとえば、3000件の社員データが格納されているテーブルから「平均年齢よりも高い社員だけを出力する」といった条件をつけることができます。サブクエリを含んだクエリは先にサブクエリから実行され、その実行結果を一つのテーブルと見なしてメインクエリが実行されます。

ただし、これはサブクエリが外側のクエリの列を使わない場合の話です。EXISTSでよく使う「外側のテーブルの列を参照するサブクエリ」は相関サブクエリと呼ばれ、外側のクエリの1行ごとにサブクエリが評価されます。くわしい動きは「EXISTSの使用例」で紹介します。

【where句のサブクエリ記述例】

select * from テーブルA
where 項目 > (select 項目 from テーブルB where 条件);

サブクエリの使用例

【売り上げテーブル】

+------------+------------+--------+
| 氏名       | 社員コード | 売上額 |
+------------+------------+--------+
| 佐藤 太郎  | 10001      | 200    |
| 鈴木 次郎  | 10002      | 150    |
| 高橋 三郎  | 10003      | 100    |
+------------+------------+--------+

【実行クエリ】

select 氏名,売上額 from 売り上げ
where 売上額 > (select AVG(売上額) from 売り上げ);

【結果】

+------------+--------+
| 氏名       | 売上額 |
+------------+--------+
| 佐藤 太郎  | 200    |
+------------+--------+

まず、(select AVG(売上額) from 売り上げ) の部分で社内全員の売上額の平均値を計算します。

この部分がサブクエリです。
そして、メインクエリで平均値よりも高い売上額を出している社員を表示しています。

SELECT文の使い方については、こちらの記事で詳しく解説しています。

▼あわせてチェック▼
【SQL】データを抽出するSELECT文の使い方を基礎から解説 【SQL】データを抽出するSELECT文の使い方を基礎から解説 ▶

あなたのご希望に沿った案件が必ず見つかります
【フリーランス向け】高収入・好待遇の案件をご紹介

TECH MANIA フリーランス

≫まずは簡単60秒で無料お問い合わせから≪

EXISTSの使用例

例として、1つ以上の商品を購入したユーザを出力します。

【ユーザテーブル】

+------------+----------+
| ユーザ名   | ユーザID |
+------------+----------+
| 佐藤 太郎  | 100001   |
| 鈴木 次郎  | 100002   |
| 高橋 三郎  | 100003   |
+------------+----------+

【注文テーブル】

+----------+--------+
| ユーザID | 注文数 |
+----------+--------+
| 100001   | 2      |
| 100003   | 1      |
+----------+--------+

【EXISTSのサンプルクエリ】

select * from ユーザ
where exists (select 1 from 注文 where ユーザ.ユーザID = 注文.ユーザID);

ポイント①

「ユーザ.ユーザID」「注文.ユーザID」という表記ですが、こちらは「テーブル名.要素名」という意味で、どのテーブルの何の項目なのかを指定しています。

ポイント②

上記2つのテーブルには、どちらにも「ユーザID」が記入されています。メインクエリで参照したテーブル上の値がサブクエリで参照したテーブル上の値と一致する場合に「TRUE」を返します。テーブル同士の紐づけをしないと、すべてのユーザが表示されてしまいます。

【結果】

+------------+----------+
| ユーザ名   | ユーザID |
+------------+----------+
| 佐藤 太郎  | 100001   |
| 高橋 三郎  | 100003   |
+------------+----------+

WHERE以下の条件が「TRUE」になるユーザのレコードを抽出します。

このクエリでは、ユーザテーブルの1行ごとにサブクエリが評価されます。1行ずつ見ると、次のように判定されています。

ユーザテーブルの行注文テーブルに同じユーザIDの行があるかEXISTSの結果出力
佐藤 太郎(100001)ある(注文数2)TRUE出力される
鈴木 次郎(100002)ないFALSE出力されない
高橋 三郎(100003)ある(注文数1)TRUE出力される

EXISTSが見ているのは「行があるかどうか」だけなので、注文数の値(2や1)は結果に影響しません。

先述のポイント②でも紹介しましたが、テーブル同士を紐づけるか否かで出力されるデータが異なります。

紐づけを行わない場合は、サブクエリ(EXISTSの後ろに書かれる括弧の内側のSQLクエリ)の結果がユーザテーブルのどの行に対しても同じになります。そのため、条件にあてはまる値が1件でも存在すればすべての行が出力され、1件も存在しなければ1行も出力されません。これを「存在判定」といいます。

【紐づけを書き忘れたクエリ】

select * from ユーザ
where exists (select 1 from 注文);

【結果】

+------------+----------+
| ユーザ名   | ユーザID |
+------------+----------+
| 佐藤 太郎  | 100001   |
| 鈴木 次郎  | 100002   |
| 高橋 三郎  | 100003   |
+------------+----------+

注文テーブルに行が1件でもあれば常に「TRUE」になるため、注文していない鈴木さんまで出力されます。「購入したユーザだけを出したいのに全員表示される」ときは、サブクエリの中に「ユーザ.ユーザID = 注文.ユーザID」の条件があるかを確認しましょう。

一方で、紐づけを行う場合は、メインクエリの1行ごとに、その行の値を使ってサブクエリが評価されます。これを「相関副問合せ(相関サブクエリ)」といいます。

「select 1」や「select *」の意味

EXISTSのサブクエリでは、具体的な値ではなく、1つ以上のレコードが存在するか否かだけが使われます。SELECTの後ろには何かしら書く必要がありますが、何を書いても結果は変わらないため、「*」や「1」を使うのが一般的です。

たとえば上のサンプルクエリのサブクエリを「select ‘ABC’ from 注文 where …」のように固定の文字列に変えても、結果は同じ佐藤さんと高橋さんの2行です。MySQL公式リファレンスのEXISTSのページでも、EXISTSのサブクエリのSELECTリストは無視されるため違いはないと説明されています。「*」と「1」のどちらを使うかは、チームの書き方に合わせれば問題ありません。

EXISTS句はどういうときに使うと便利?

SQLを使用する際に、実際にEXISTS句を利用することになるケースの例を挙げてみました!

EXISTSは、「別のテーブルに関連するデータがあるかどうか」でレコードを絞り込みたいケースで使用することが多くなります。

  • 「ユーザ」のうち少なくとも1つの商品を購入した「ユーザ」を出力したい
  • 「社員データ」のうち、「部署データ」に登録されている部署に所属している「社員レコード」を出力したい
  • 「企業データ」のうち、「求人データ」に「カテゴリー」が「IT」の求人を1件以上掲載している「企業レコード」を出力したい
  • 「社員データ」と「部署データ」のうち、「社員データ」の「ID」と「部署データ」の「社員ID」が一致し、且つ、「部署コード」が「3」の「社員レコード」を出力したい

実際のシステムでは、次のような場面でよく使われます。

在庫管理システム

在庫管理システムでは、EXISTS句を用いることで、サブクエリで「在庫テーブル」のうち、該当する商品在庫が1つ以上あるかどうかを確認できます。

データ分析

データ分析では、特定の条件を満たすデータがあるかどうかを調べるのにEXISTS句が利用されます。たとえば、「商品購入テーブル」対象の期間で特定の商品を購入したユーザを調べるといったこともできます。

NOT EXISTS句とは?

NOT EXISTS句とは、EXISTS句と反対の機能を持ち、特定の条件にあてはまるレコードが存在しない場合に「TRUE」を返し、存在する場合に「FALSE」を返します。

「一度も注文していないユーザ」「まだ一度も使われていないデータ」のように、相手のテーブルに対応する行が1件もないレコードを探したいときに使います。

NOT EXISTSの使用例

【ユーザテーブル】

+------------+----------+
| ユーザ名   | ユーザID |
+------------+----------+
| 佐藤 太郎  | 100001   |
| 鈴木 次郎  | 100002   |
| 高橋 三郎  | 100003   |
+------------+----------+

【注文テーブル】

+----------+--------+
| ユーザID | 注文数 |
+----------+--------+
| 100001   | 2      |
| 100003   | 1      |
+----------+--------+

【NOT EXISTSを使用するクエリ例】

select * from ユーザ
where not exists (select 1 from 注文 where ユーザ.ユーザID = 注文.ユーザID);

【結果】

+------------+----------+
| ユーザ名   | ユーザID |
+------------+----------+
| 鈴木 次郎  | 100002   |
+------------+----------+

注文テーブルに行がない鈴木さんだけが「TRUE」になり、出力されます。EXISTSの使用例で出力された佐藤さん・高橋さんとはちょうど逆の結果です。EXISTSと同じく、サブクエリ内の「ユーザ.ユーザID = 注文.ユーザID」を書き忘れると、注文テーブルに1件でも行があれば1行も出力されなくなるので注意しましょう。

EXISTS句と類似文法の比較

EXISTSとINの比較

EXISTS句とIN句はどちらもサブクエリを扱いますが、EXISTS句が存在の有無のみを出力するのに対し、IN句は条件にあてはまるコードをすべて出力し、メインクエリで値を比較します。また、IN句はカラム名を指定する必要がありますが、EXISTS句では不要です。EXISTS句は条件にあてはまるレコードが存在した時点で検索を打ち切るため、IN句よりもパフォーマンスが優れているといわれています。そのため、具体的な値を比較する場合はIN句、特定の条件にあてはまるレコードの有無のみを確認する場合にEXISTS句を使いましょう。

なお、MySQL 8.0.16以降では、EXISTSのサブクエリにもINのサブクエリと同じ最適化(セミ結合への変換)が適用されるとMySQL公式リファレンスの準結合変換のページに記載されています。現在のMySQLでは書き方だけで速さが決まるとは限らないため、速度が気になる場合はEXPLAINで実行計画を確認しましょう。

「注文したことのあるユーザ」をINで書くと、次のようになります。結果はEXISTSの使用例と同じく、佐藤さんと高橋さんの2行です。

select * from ユーザ
where ユーザID in (select ユーザID from 注文);

IN句の使い方やサブクエリの活用方法については、こちらの記事で詳しく解説しています。

▼あわせてチェック▼
【SQL入門】IN句の使い方やサブクエリの活用方法を解説 【SQL入門】IN句の使い方やサブクエリの活用方法を解説 ▶

否定形の「NOT IN」と「NOT EXISTS」には、NULLの扱いに大きな違いがあります。たとえば、注文テーブルにユーザIDがNULLの行が1件まぎれ込んでいたとします。

【注文テーブル(ユーザIDがNULLの行を追加)】

+----------+--------+
| ユーザID | 注文数 |
+----------+--------+
| 100001   | 2      |
| 100003   | 1      |
| NULL     | 3      |
+----------+--------+

【NOT INとNOT EXISTSの比較】

-- NOT IN:1行も返らない(Empty set)
select * from ユーザ
where ユーザID not in (select ユーザID from 注文);

-- NOT EXISTS:鈴木 次郎(100002)が返る
select * from ユーザ
where not exists (select 1 from 注文 where ユーザ.ユーザID = 注文.ユーザID);

NOT INは「100002は、100001でも100003でもNULLでもない」という比較を行います。SQLではNULLとの比較結果が「TRUE」にも「FALSE」にもならない(不明)ため、条件全体が「TRUE」にならず、鈴木さんも出力されません。一方、NOT EXISTSは「同じユーザIDの行があるか」だけを見るので、NULLの行があっても期待どおりの結果になります。

サブクエリの列にNULLが入る可能性がある場合は、NOT INではなくNOT EXISTSを使うのが安全です。NULLを含む場合のNOT INの結果については、PostgreSQL公式ドキュメントのNOT INの説明でも解説されています。

NULLの扱い方は、こちらの記事で詳しく解説しています。

▼あわせてチェック▼
【SQL】NULLの扱い方をサンプルコードで分かりやすく解説 【SQL】NULLの扱い方をサンプルコードで分かりやすく解説 ▶

EXISTSとJOINの比較

JOIN句は複数のテーブルを結合して1つの結果を出力しますが、EXISTS句はテーブルの結合をしません。よって、結合結果全体を出力させる場合はJOIN句、特定の条件にあてはまるレコードの有無のみを知りたい場合はEXISTS句を使いましょう。

結果の違いがはっきり出るのは、相手のテーブルに一致する行が複数あるときです。例として、佐藤さんがもう1回注文し、注文テーブルが次の3行になったとします。

【注文テーブル(佐藤さんの注文を1行追加)】

+----------+--------+
| ユーザID | 注文数 |
+----------+--------+
| 100001   | 2      |
| 100003   | 1      |
| 100001   | 5      |
+----------+--------+

【INNER JOINで書いた場合】

select ユーザ.ユーザ名, 注文.注文数 from ユーザ
inner join 注文 on ユーザ.ユーザID = 注文.ユーザID
order by ユーザ.ユーザID, 注文.注文数;

【結果】

+------------+--------+
| ユーザ名   | 注文数 |
+------------+--------+
| 佐藤 太郎  | 2      |
| 佐藤 太郎  | 5      |
| 高橋 三郎  | 1      |
+------------+--------+

JOINは一致した行の数だけ結果を返すため、注文が2件ある佐藤さんは2行出力されます。一方、同じ注文テーブルに対してEXISTSの使用例と同じクエリを実行すると、結果は佐藤さんと高橋さんの2行のままで、佐藤さんは1行しか出力されません。EXISTSは「一致する行があるか」だけを判定するためです。

「注文したことのあるユーザの一覧」のように、相手のテーブルの列が不要で、重複させたくない場合はEXISTSが向いています。注文数のように相手のテーブルの列も表示したい場合はJOINを使いましょう。

また、NOT EXISTSは、LEFT JOIN(左外部結合)と「IS NULL」の組み合わせで書き換えることもできます。LEFT JOINでは、相手のテーブルに一致する行がない場合でも左側のテーブルの行は残り、相手側の列はNULLになります。この性質を使って、注文テーブル側がNULLの行だけを取り出します(注文テーブルは元の2行の状態です)。

【NOT EXISTSと同じ結果になるLEFT JOIN】

select ユーザ.* from ユーザ
left join 注文 on ユーザ.ユーザID = 注文.ユーザID
where 注文.ユーザID is null;

【結果】

+------------+----------+
| ユーザ名   | ユーザID |
+------------+----------+
| 鈴木 次郎  | 100002   |
+------------+----------+

結果はNOT EXISTSの使用例と同じです。どちらでも書けますが、「注文が存在しないユーザ」という意図がそのまま読み取れるNOT EXISTSのほうが、初心者には分かりやすいでしょう。

JOINの使い方については、こちらの記事で詳しく解説しています。

▼あわせてチェック▼
【SQL入門】JOINでSQLのテーブルを結合する(INNER JOIN, OUTER JOIN) 【SQL入門】JOINでSQLのテーブルを結合する(INNER JOIN, OUTER JOIN) ▶

EXISTSと通常の比較演算子の比較

比較演算子は2つの値を比較して出力を行います。そのため、値同士を比較する必要がある場合は比較演算子、特定の条件にあてはまるレコードの有無のみを知りたい場合はEXISTS句を使いましょう。

UPDATE文・DELETE文との組み合わせ

実は、EXISTSを使うのはSELECT文だけではありません。メインクエリにUPDATE文やDELETE文を用いることで、該当するデータのみを更新・削除することもできます。

UPDATE文でEXISTSを使う

メインクエリにUPDATE文を用いると、サブクエリの条件にあてはまる行だけを更新できます。

【構文例】

update テーブル名 set カラム名 = 値 where exists ( サブクエリ );

よく使うのが、「SET句のサブクエリで値を計算し、WHERE EXISTSで更新対象を絞る」という形です。例として、ユーザテーブルに「注文数合計」列を追加し、注文テーブルから集計した値を書き込んでみます。

【ユーザテーブル(注文数合計列を追加、初期値は0)】

+------------+----------+------------+
| ユーザ名   | ユーザID | 注文数合計 |
+------------+----------+------------+
| 佐藤 太郎  | 100001   | 0          |
| 鈴木 次郎  | 100002   | 0          |
| 高橋 三郎  | 100003   | 0          |
+------------+----------+------------+

まず、WHERE EXISTSを付けずに更新すると次のようになります。

【WHERE EXISTSがないUPDATE文】

update ユーザ
set 注文数合計 = (select sum(注文数) from 注文 where 注文.ユーザID = ユーザ.ユーザID);

【結果】

+------------+----------+------------+
| ユーザ名   | ユーザID | 注文数合計 |
+------------+----------+------------+
| 佐藤 太郎  | 100001   | 2          |
| 鈴木 次郎  | 100002   | NULL       |
| 高橋 三郎  | 100003   | 1          |
+------------+----------+------------+

WHERE句がないため、ユーザテーブルの全行が更新対象になります。注文がない鈴木さんはサブクエリの集計結果がNULLになり、元の0がNULLで上書きされてしまいました。

注文があるユーザだけを更新したい場合は、WHERE EXISTSで対象を絞ります。

【WHERE EXISTSで更新対象を絞ったUPDATE文】

update ユーザ
set 注文数合計 = (select sum(注文数) from 注文 where 注文.ユーザID = ユーザ.ユーザID)
where exists (select 1 from 注文 where 注文.ユーザID = ユーザ.ユーザID);

【結果】

+------------+----------+------------+
| ユーザ名   | ユーザID | 注文数合計 |
+------------+----------+------------+
| 佐藤 太郎  | 100001   | 2          |
| 鈴木 次郎  | 100002   | 0          |
| 高橋 三郎  | 100003   | 1          |
+------------+----------+------------+

鈴木さんの行は更新対象から外れたため、0のまま残っています。

なお、注文数合計列にNOT NULL制約が付いている場合、MySQLの初期設定(MySQL公式リファレンスのSQLモードで説明されているSTRICTモード)では1つ目のUPDATE文は「ERROR 1048 (23000): Column ‘注文数合計’ cannot be null」となり、更新されません。どちらの場合も、WHERE EXISTSで対象を絞っておけば防げます。

UPDATE文は、WHERE句の書き方を間違えると意図しない行まで変更してしまいます。MySQLでは実行後に「Rows matched(条件に一致した行数)」と「Changed(実際に変更された行数)」が表示されるので、想定どおりの件数かを確認しましょう。WHERE EXISTSを付けたUPDATE文では、どちらも2(佐藤さんと高橋さん)になります。

UPDATE文の基本的な使い方は、こちらの記事で詳しく解説しています。

▼あわせてチェック▼
【SQL】UPDATE文の基本から応用まで解説!データベースのレコードを条件に合わせて更新する 【SQL】UPDATE文の基本から応用まで解説!データベースのレコードを条件に合わせて更新する ▶

DELETE文でEXISTSを使う

DELETE文と組み合わせると、条件にあてはまる行だけを削除できます。特にNOT EXISTSと組み合わせて、「関連するデータが1件もないレコード」を削除する使い方が一般的です。例として、一度も注文していないユーザをユーザテーブルから削除します(注文テーブルは元の2行の状態です)。

【NOT EXISTSを使ったDELETE文】

delete from ユーザ
where not exists (select 1 from 注文 where 注文.ユーザID = ユーザ.ユーザID);

【実行後のユーザテーブル】

+------------+----------+
| ユーザ名   | ユーザID |
+------------+----------+
| 佐藤 太郎  | 100001   |
| 高橋 三郎  | 100003   |
+------------+----------+

注文がない鈴木さんの1行だけが削除されました。MySQLでは実行後に「Query OK, 1 row affected」と表示され、削除された行数を確認できます。

削除した行は元に戻せないため、実行前に確認しておくことが大切です。このDELETE文のWHERE句は「NOT EXISTSの使用例」のSELECT文と同じ条件なので、先にそのSELECT文を実行すれば、削除される行(鈴木さんの1行)を事前に確認できます。サブクエリ内の紐づけ条件を書き忘れると、注文テーブルに1行でもある限り1行も削除されず、逆に注文テーブルが空だとユーザ全員が削除されてしまう点にも注意しましょう。

DELETE文の使い方については、こちらの記事で詳しく解説しています。

▼あわせてチェック▼
【SQL】データ削除「DELETE文」の使い方。全件削除・複数テーブル参照も解説 【SQL】データ削除「DELETE文」の使い方。全件削除・複数テーブル参照も解説 ▶

CASE式との組み合わせ

SQLで条件分岐をするときにはCASE式を使いますが、このCASE式の中でもEXISTSを使うことができます。

【構文例】

case
    when exists (サブクエリ1) then '値1'
    when exists (サブクエリ2) then '値2'
    ...
    else '例外値'
end

CASE式は最後に必ず「end」を書きます。WHENは上から順に判定され、最初に「TRUE」になったWHENの値が返ります。どれにもあてはまらない場合はELSEの値が返ります。

WHERE句のEXISTSは「条件にあてはまる行だけを残す」使い方でしたが、CASE式と組み合わせると、全員を出力したうえで「あり/なし」の列を追加できます。

【CASE式とEXISTSを組み合わせたクエリ例】

select ユーザ名,
  case
    when exists (select 1 from 注文 where 注文.ユーザID = ユーザ.ユーザID) then '購入あり'
    else '購入なし'
  end as 購入状況
from ユーザ;

【結果】

+------------+----------+
| ユーザ名   | 購入状況 |
+------------+----------+
| 佐藤 太郎  | 購入あり |
| 鈴木 次郎  | 購入なし |
| 高橋 三郎  | 購入あり |
+------------+----------+

注文の件数や注文数には関係なく、注文テーブルに1行でもあれば「購入あり」になります。

CASE式については以下の記事でも詳しく解説しています。こちらも合わせてお読みください。

▼あわせてチェック▼
SQL|CASE式で条件分岐を処理する【SELECT句からUPDATE句まで詳しく解説】 SQL|CASE式で条件分岐を処理する【SELECT句からUPDATE句まで詳しく解説】 ▶

EXISTSを使うときに気を付けることは?

非常に便利なEXISTS句ですが、クエリの中に組み込む際はいくつか注意すべきポイントがあります。

クエリを工夫しないとパフォーマンスが下がりやすい

EXISTSを使用してレコードの有無を確認すると、参照したテーブルの総レコード数に応じて比較処理が実行されます。指定した条件に合致するレコードがあればそこで処理は終了しますが、存在しない場合は全てのレコードをチェックするので、レコード数が多くなればなるほど処理は遅くなりやすいという欠点があります。

また、外側のテーブルの列を参照する相関サブクエリは、外側のクエリの1行ごとに評価されます。今回のユーザテーブルは3行なので気になりませんが、外側のテーブルが数十万行あると、そのぶんサブクエリの評価回数も増えて遅くなることがあります。

実際のクエリで使用する場合には、サブクエリで紐づけに使う列(今回の例では注文テーブルのユーザID)に適切なインデックスを設定し、外側のクエリもWHERE句で必要な行に絞り込むなどの工夫をするように意識しましょう。遅いと感じたときは、EXPLAINで実行計画を確認するのがおすすめです。EXPLAINの使い方はMySQL公式リファレンスのEXPLAINのページで確認できます。

EXISTSもセキュリティの脆弱性につながる場合がある

SQLには「SQLインジェクション」というサイバー攻撃の手法があり、適切な対策を取らないと機密情報の流出やデータベースの破壊といった損害を受ける場合があります。

EXISTSを使用する場合も例外ではなく、データベースに情報を入力する際は記号をエスケープ(プログラムにおける各種記号の文字が持つ機能を喪失させ、ただの文字と認識させるようにする処理)したり、プレースホルダー(特定の記号や文字を事前に入れておき、実際の値はその文字にあとから代入する方法)を用いたりするなどの対策を取る必要があります。具体的な対策は、IPA(情報処理推進機構)の「安全なウェブサイトの作り方」のSQLインジェクションのページでも解説されています。

特にSQLインジェクションによるサイバー攻撃は毎年多くの企業で発生し、多大な損害を与えている攻撃手法です。業務でSQLを使う場合には、セキュリティ面もしっかりと意識しておきたいですね。

まとめ

EXISTSは、サブクエリが1行以上返すかどうかだけを判定する句です。サブクエリの中で外側のテーブルと紐づける条件を書き、外側のテーブルの1行ごとに「対応する行があるか」を調べるのが基本の使い方です。似た書き方との使い分けは次のとおりです。

やりたいことおすすめの書き方
関連するデータが存在するレコードだけを取り出したいEXISTS
関連するデータが1件もないレコードを取り出したいNOT EXISTS(NULLがあると結果が変わるNOT INより安全)
決まった値のリストに含まれるかで絞り込みたいIN
相手のテーブルの列も一緒に表示したいJOIN(一致する行の数だけ結果が増える点に注意)
存在するかどうかで更新・削除の対象を絞りたいUPDATE文・DELETE文のWHERE句でEXISTS / NOT EXISTS
存在するかどうかで表示する値を変えたいCASE式の中でEXISTS

まずは記事内のユーザテーブルと注文テーブルを作って、紐づけ条件を付けた場合と外した場合で結果がどう変わるかを試してみてください。

SQL関連記事

SQLの勉強方法は?

書籍やインターネットで学習する方法があります。昨今では、YouTubeなどの動画サイトやエンジニアのコミュニティサイトなども充実していて多くの情報が手に入ります。
そして、より効率的に知識・スキルを習得するには、知識をつけながら実際に手を動かしてみるなど、インプットとアウトプットを繰り返していくことが重要です。特に独学の場合は、有識者に質問ができたりフィードバックをもらえるような環境があると、理解度が深まるでしょう。

ただ、SQLに限らず、ITスキルを身につける際、どうしても課題にぶつかってしまうことはありますよね。特に独学だと、わからない部分をプロに質問できる機会を確保しにくく、モチベーションが続きにくいという側面があります。独学でモチベーションを維持する自信がない人にはプログラミングスクールという手もあります。費用は掛かりますが、その分スキルを身につけやすいです。しっかりと知識・スキルを習得して実践に活かしたいという人はプログラミングスクールがおすすめです。

プログラミングスクールならテックマニアがおすすめ!

ITスキル需要の高まりとともにプログラミングスクールも増えました。しかし、どのスクールに通うべきか迷ってしまう人もいるでしょう。そんな方にはテックマニアをおすすめします!これまで多くのITエンジニアを育成・輩出してきたテックマニアでもプログラミングスクールを開講しています。

<テックマニアの特徴>
・たしかな育成実績と親身な教育 ~セカンドキャリアを全力支援~
・講師が現役エンジニア ~“本当”の開発ノウハウを直に学べる~
・専属講師が学習を徹底サポート ~「わからない」を徹底解決~
・実務ベースでスキルを習得 ~実践的な凝縮カリキュラム~

このような特徴を持つテックマニアはITエンジニアのスタートラインとして最適です。
話を聞きたい・詳しく知りたいという方はこちらからお気軽にお問い合わせください。

     

Otherカテゴリの最新記事