コマンド道場

INTERSECT(両方に共通する行だけを残す)の使い方

両方にある行だけが残る

shain給与 50 万以上

name
中村
井上
山本

shain在籍中

name
中村
井上
吉田

INTERSECT両方に当てはまる人

name
中村
井上
  • 給与 50 万以上 10 在籍中 16両方に当てはまる人 9
  • 残るのは両方にある行だけです。給与 50 万以上だけにある 1 行と、在籍中だけにある 7 行は消えます

10 人と 16 人から残るのは 9 人です。給与 50 万以上でも退職していれば消え、在籍中でも給与が届かなければ消えます。

INTERSECT は 2 つの結果の共通部分だけを残します。 片方にしか無い行はすべて消えます。同じことは AND でも書けることが多いのですが、別々に取り出した 2 つの一覧を突き合わせたいときは、こちらのほうが素直に書けます。

未確認の製品があります そのまま使える: PostgreSQL / MySQL / MariaDB / SQLite / SQL Server / Oracle Database

未確認: IBM Db2

製品ごとの対応表(根拠つき)を見る

このページの実行結果と図は、すべて本物のデータベースに同じ SQL を流して得たものです。手で書いた「たぶんこうなる」ではありません。

01片方にしか無い行は消える

結論: INTERSECT は、左右の両方に現れる行だけを残します。

縦につなぐという点では UNION と同じですが、残す行の決め方が逆向きです。UNION が「どちらかにあれば残す」なのに対し、INTERSECT は「両方にあれば残す」です。

左右を入れ替えても結果は同じです。「両方にある」という条件に前後は関係ないからです。

⚠️ 比べるのは行まるごとです。選んでいる列の値がすべて一致して初めて「同じ行」とみなされます。列を 1 つ足すと共通の行が急に減ることがあります。

1 行ずつ、何をしているか
  1. SELECT name FROM shain1 つ目の一覧
  2. WHERE salary >= 500000給与 50 万以上(10 人)
  3. INTERSECT★両方にある行だけを残す
  4. SELECT name FROM shain2 つ目の一覧
  5. WHERE taishoku_on IS NULL在籍中(16 人)
  6. ORDER BY 1;並べ替えは最後に 1 つだけ

左右は独立した SELECT です。両方に出てくる名前だけが結果に残ります。

給与 50 万以上で、かつ在籍中の人
SELECT name FROM shain WHERE salary >= 500000
INTERSECT
SELECT name FROM shain WHERE taishoku_on IS NULL
ORDER BY 1;
name
中村
井上
伊藤
佐藤
加藤
山田
清水
田中
鈴木

9

9 人です。給与 50 万以上の 10 人のうち、山本だけが退職しているので消えました。

左右を入れ替えても同じ
SELECT name FROM shain WHERE taishoku_on IS NULL
INTERSECT
SELECT name FROM shain WHERE salary >= 500000
ORDER BY 1;
name
中村
井上
伊藤
佐藤
加藤
山田
清水
田中
鈴木

9

同じ 9 人です。「両方にある」に前後は関係ありません。EXCEPT はこうならないので、あとで見比べてください。

021 つの表なら AND でも書ける

結論: 同じ表に対する条件の重ね合わせなら、AND で書いたほうが短く済みます。

上の例は WHERE salary >= 500000 AND taishoku_on IS NULL と書いても同じ 9 人になります。表を 2 回読む必要も無いので、こちらのほうが素直です。

INTERSECT が効くのは、AND では書けない形のときです。

  • 別々の表から取り出した一覧を突き合わせたい
  • 集計の単位が違う(2023 年に受注がある顧客と、2024 年に受注がある顧客 など)
  • 同じ行に並べられない条件を、いったん別々の一覧にしてから比べたい

⚠️ 逆に言えば、1 つの行だけで判定できる条件を INTERSECT で書くのは遠回りです。読む人に「なぜ 2 回読んでいるのか」を考えさせることになります。

同じことを AND で書く
SELECT name FROM shain
WHERE salary >= 500000
  AND taishoku_on IS NULL
ORDER BY 1;
name
中村
井上
伊藤
佐藤
加藤
山田
清水
田中
鈴木

9

同じ 9 人です。1 つの表の 1 つの行で判定できる条件なので、こちらで足ります。

AND では書けない例(別々の表を突き合わせる)
SELECT busho_id FROM shain
INTERSECT
SELECT busho_id FROM busho
ORDER BY 1;
busho_id
1
2
3
4
5
7

6

6 行です。社員が 1 人でもいる部署番号だけが残ります。法務(6)は部署表にはありますが社員表に無いので消えています。

03重複は 1 つにまとめられる

結論: INTERSECT の結果に同じ行は 2 つ出てきません。

UNION と同じで、集合演算は既定で重複を消します。左に同じ値が 4 行あっても、右にその値があれば結果は 1 行です。

「何件あるか」を数える目的では使えません。 件数を保ちたいなら結合や EXISTS を使ってください。

⚠️ ここは UNION ALL のような「消さない版」が用意されていない点にも注意してください。

左に 4 行あっても結果は 1 行
SELECT busho_id FROM shain WHERE busho_id = 2
INTERSECT
SELECT busho_id FROM shain WHERE salary < 600000;
busho_id
2

1

1 行です。開発には 4 人いて左は 4 行返しますが、値がすべて 2 なので 1 行にまとめられます。

04⚠️ よくある間違い:NULL 同士は「同じ」とみなされる

結論: 集合演算では NULL と NULL が「同じ行」として扱われます。

WHERE の比較とは扱いが違います。busho_id = NULL は決して真になりませんが、集合演算の重複判定では NULL 同士は一致します。

そのため、左右の両方に「所属が未設定」の行があれば、その行は共通部分として残ります。

この違いは覚えるしかありません。 「比較の =」と「集合演算の同一性」は別のルールで動いています。

NULL 同士が共通部分になる
SELECT busho_id FROM shain WHERE busho_id IS NULL
INTERSECT
SELECT busho_id FROM shain WHERE busho_id IS NULL;
busho_id
NULL

1

1 行返り、その値は NULL です。比較の = なら真にならない組み合わせが、ここでは一致として扱われています。

自分で打ってみる

このページの例と同じデータが入った本物のデータベースを、この場(あなたのブラウザの中)で起動できます。打った SQL がサーバーへ送られることはありません。

押すと初回だけ約 9MB を読み込みます(2 回目以降はキャッシュされます)。

解いてみる

読んだだけでは書けるようになりません。実行結果で採点します。

関連するトピック

  • UNION / UNION ALLどちらかにあれば残す集合演算です。
  • EXCEPT片方にしかない行を残す集合演算です。
  • IN / EXISTS「あるかどうか」で絞る別の書き方です。
  • AND / OR / NOTAND で条件を重ねる書き方はこちらです。
  • IS NULLNULL の扱いそのものはこちらです。

根拠(一次情報)