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 つ足すと共通の行が急に減ることがあります。
SELECT name FROM shain1 つ目の一覧WHERE salary >= 500000給与 50 万以上(10 人)INTERSECT★両方にある行だけを残すSELECT name FROM shain2 つ目の一覧WHERE taishoku_on IS NULL在籍中(16 人)ORDER BY 1;並べ替えは最後に 1 つだけ
左右は独立した SELECT です。両方に出てくる名前だけが結果に残ります。
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 回読んでいるのか」を考えさせることになります。
SELECT name FROM shain
WHERE salary >= 500000
AND taishoku_on IS NULL
ORDER BY 1;| name |
|---|
| 中村 |
| 井上 |
| 伊藤 |
| 佐藤 |
| 加藤 |
| 山田 |
| 清水 |
| 田中 |
| 鈴木 |
(9 行)
同じ 9 人です。1 つの表の 1 つの行で判定できる条件なので、こちらで足ります。
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 のような「消さない版」が用意されていない点にも注意してください。
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 同士は一致します。
そのため、左右の両方に「所属が未設定」の行があれば、その行は共通部分として残ります。
この違いは覚えるしかありません。 「比較の =」と「集合演算の同一性」は別のルールで動いています。
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 の扱いそのものはこちらです。
根拠(一次情報)
- PostgreSQL 18 マニュアル: 問い合わせの結合(UNION / INTERSECT / EXCEPT)一次情報・確認 2026-08-11
- PostgreSQL 18 マニュアル: 比較演算子と NULL の扱い一次情報・確認 2026-08-11