はじめてのSQL【中級編:述語について②】
こんにちは。
スタッフブログ担当の岩でございます。
前回の記事では「LIKE」、「BETWEEN」、「IS NULL」、「IS NOT NULL」という述語について説明いたしました。

今回は、「IN」、「EXISTS」という述語について説明いたします。
今回はShopテーブルとTenpoShohinテーブルを使って説明します。
shohin_id (商品ID) | shohin_mei (商品名) | shohin_bunrui (商品分類) | hanbai_tanka (販売単価) | shiire_tanka (仕入単価) | torokubi (登録日) |
0001 | ボールペン | 事務用品 | 100 | 50 | 2020-10-20 |
0002 | おろしがね | キッチン用品 | 500 | 350 | 2020-09-07 |
0003 | フォーク | キッチン用品 | 100 | 30 | |
0004 | 圧力鍋 | キッチン用品 | 5000 | 1000 | 2020-04-25 |
0005 | 包丁 | キッチン用品 | 8000 | | 2020-07-05 |
0006 | カッターシャツ | 衣類 | 3000 | 1500 | 2020-06-11 |
0007 | 穴あけパンチ | 事務用品 | 400 | 240 | 2020-11-27 |
0008 | Tシャツ | 衣類 | 1500 | | 2020-03-13 |
tenpo_id (店舗ID) | tenpo_mei (店舗名) | shohin_id (商品ID) | suryo (数量) |
A | 東京 | 0001 | 30 |
A | 東京 | 0002 | 50 |
A | 東京 | 0003 | 15 |
B | 名古屋 | 0002 | 30 |
B | 名古屋 | 0004 | 120 |
B | 名古屋 | 0005 | 20 |
C | 大阪 | 0004 | 50 |
C | 大阪 | 0006 | 90 |
C | 大阪 | 0007 | 70 |
IN述語

IN述語はORを省略して記述することができます。
まず、ORを使ってShopテーブルから「仕入単価が50円、240円、350円」のデータを選択しましょう。
SELECT shohin_id, shohin_mei, shiire_tanka
FROM Shop
WHERE shiire_tanka = 50
OR shiire_tanka = 240
OR shiire_tanka = 350;
shohin_id (商品ID) | shohin_mei (商品名) | shiire_tanka (仕入単価) |
0001 | ボールペン | 50 |
0002 | おろしがね | 350 |
0007 | 穴あけパンチ | 240 |
この書き方の欠点は、選択対象としたい値が増えるにつれてSQLもどんどん長くなってしまうことです。
こんなときはIN述語を使うとすっきりまとまります。
SELECT shohin_id, shohin_mei, shiire_tanka
FROM Shop
WHERE shiire_tanka IN (50, 240, 350);
反対に「仕入単価が50円、240円、350円以外」のデータを選択したいときは、
否定形の「NOT IN」を使います。
SELECT shohin_id, shohin_mei, shiire_tanka
FROM Shop
WHERE shiire_tanka NOT IN (50, 240, 350);
shohin_id (商品ID) | shohin_mei (商品名) | shiire_tanka (仕入単価) |
0003 | フォーク | 30 |
0004 | 圧力鍋 | 1000 |
0006 | カッターシャツ | 1500 |
INとNOT INどちらの場合でも、NULLを選択することはできないことに注意してください。
INとNOT INどちらの結果にも、仕入単価がNULLのデータは選択されていません。
NULLはあくまでIS NULL、IS NOT NULLで判別するのが決まりです。
INとサブクエリ
IN述語、NOT IN述語は、引数にサブクエリを指定することができます。
実際の使い方をTenpoShohinテーブルを使ってみてみましょう。
例1.大阪店(tenpo_id = ‘C’)に置いている商品(shohin_id)の販売単価(hanbai_tanka)を求める
SELECT shohin_id, shohin_mei, hanbai_tanka
FROM Shohin
WHERE shohin_id IN (
SELECT shohin_id
FROM TenpoShohin
WHERE tenpo_id = 'C'
);
shohin_id (商品ID) | shohin_mei (商品名) | hanbai_tanka (販売単価) |
0004 | 圧力鍋 | 5000 |
0006 | カッターシャツ | 3000 |
0007 | 穴あけパンチ | 400 |
この例1.の問題を解くためには2つのステップがあります。
1.TempoShohinテーブルから大阪店(tenpo_id = ‘C’)が持っている商品(shohin_id)を選択する。
2.Shopテーブルから 1. で選択した商品(shohin_id)のみ販売単価(hanbai_tanka)を選択する。
1つ目のステップにあたるのがIN述語の引数で指定しているサブクエリです。
サブクエリは内側から実行されていくので、一番最初に実行されるのは以下の部分です。
SELECT shohin_id
FROM TenpoShohin
WHERE tenpo_id = 'C';
shohin_id(商品ID) |
0004 |
0006 |
0007 |
サブクエリを展開したSQL文は以下の通りです。
SELECT shohin_id, shohin_mei, hanbai_tanka
FROM Shohin
WHERE shohin_id IN ('0004','0006','0007');
展開した形でSQLを書けるなら、サブクエリを使う必要はないのではないか?
と思われるかもしれませんが、
「TenpoShohinテーブルのデータが絶対に変更されない。」という条件が
あればサブクエリを使う必要はありません。
実際には各店舗で扱う商品は変わっていくでしょうから、TenpoShohinテーブル内の大阪店の扱う
商品も変化していくと思います。
その場合、サブクエリを使わないと商品が変更される度に、SQL文を修正しなければなりません。
一方、サブクエリを使ってSQL文を作成しておけば、データがどれだけ変更されても
ずっと同じSQL文を使い続けることができます。
このように、データの変更に強いという利点を持つプログラムを
「保守性に優れる」、「メンテナンスフリー」と呼びます。
EXISTS述語

実はEXISTSを使わなくてもINやNOT INでほぼ代用できます。
しかし、完全に代用できるわけではありません。
EXISTS述語は「条件に合致するレコードの存在有無を調べること」です。
条件に合致するレコードが存在すれば真(TRUE)、存在しなければ偽(FALSE)を返します。
INとサブクエリで求めたものと同じ
「大阪店(tenpo_id = ‘C’)に置いている商品(shohin_id)の販売単価(hanbai_tanka)」
をEXISTSを使って求めてみましょう。
SELECT shohin_id, shohin_mei, hanbai_tanka
FROM Shop AS S
WHERE EXISTS (
SELECT 1
FROM TenpoShohin AS TS
WHERE TS.tenpo_id = 'C'
AND TS.shohin_id = S.shohin_id
);
shohin_id (商品ID) | shohin_mei (商品名) | hanbai_tanka (販売単価) |
0004 | 圧力鍋 | 5000 |
0006 | カッターシャツ | 3000 |
0007 | 穴あけパンチ | 400 |
EXISTSは必ず右側に相関サブクエリを記述します。
今回のSQL文でも「TS.shohin_id = S.shohin_id」という風にShopテーブルとTenpoShohinテーブルを結合しています。
また、サブクエリのSELECT部分で「SELECT 1」と記述しています。
これはどの行も選択していませんが、EXISTSはレコードの存在有無しか確認しないため、
どのような行を選択していても一切関係ありません。
そのため、以下のような書き方をしても結果は変わりません。
SELECT shohin_id, shohin_mei, hanbai_tanka
FROM Shop AS S
WHERE EXISTS (
SELECT *
FROM TenpoShohin AS TS
WHERE TS.tenpo_id = 'C'
AND TS.shohin_id = S.shohin_id
);
INをEXISTSで書き換えられたように、NOT INを「NOT EXISTS」で書き換えることも可能です。
「東京店(tenpo_id = ‘A’)に置いている商品(shohin_id)以外の販売単価(hanbai_tanka)」
をNOT EXISTSを使って記述します。
SELECT shohin_id, shohin_mei, hanbai_tanka
FROM Shop AS S
WHERE NOT EXISTS (
SELECT 1
FROM TenpoShohin AS TS
WHERE TS.tenpo_id = 'A'
AND TS.Shohin_id = S.Shohin_id
);
shohin_id (商品ID) | shohin_mei (商品名) | hanbai_tanka (販売単価) |
0004 | 圧力鍋 | 5000 |
0005 | 包丁 | 8000 |
0006 | カッターシャツ | 3000 |
0007 | 穴あけパンチ | 400 |
0008 | Tシャツ | 1500 |
まとめ
今回は「IN」と「EXISTS」を一緒に説明いたしました。
「IN」の方が分かりやすいなと感じる方が多いのではないでしょうか。
しかし、「EXISTS」には「IN」にはない便利さがあり、このふたつは厳密に同値ではないので
どちらの述語もマスターしていただければと思います。
最後までお読みいただきありがとうございました。