集合演算 UNION/INTERSECT/EXCEPT
集合演算 UNION/INTERSECT/EXCEPT とは
UNION、INTERSECT、EXCEPTを使ったSQL集合演算を実践的に学習します。本レッスンでは、集合演算 UNION/INTERSECT/EXCEPT の基本から実際の使いどころまでを整理し、現場で迷わず使える形に落とし込みます。
なぜ重要か
結合 (JOIN) は「横につなぐ」操作ですが、集合演算 は「縦に重ねる」操作です。「店舗 A の顧客と店舗 B の顧客を 1 つのリストにまとめる」「両店舗共通の顧客だけを出す」「片方にしかいない顧客を出す」など、リスト同士を合成・比較するときに使います。レポート作成や差分抽出で頻出するので、JOIN とセットで必ず覚えておきましょう。集合演算は数学の集合論にそのまま対応しており、ベン図で考えると挙動が分かりやすくなります。
3 つの演算子
| 演算子 | 意味 |
|---|---|
| UNION | 2 つの結果を縦に合成 (重複は除去) |
| UNION ALL | 縦に合成 (重複もそのまま残す) |
| INTERSECT | 両方に共通する行だけ |
| EXCEPT | 左にあるが右にない行 (MySQL 8 以降 / PostgreSQL) |
MySQL では INTERSECT と EXCEPT はどちらも 8.0.31 で同時に追加されました。それより前のバージョンでは使えません。PostgreSQL は古くからすべて対応しています。
構文
2 つの SELECT 文を演算子でつなぎます。列数と列の型が一致している必要があります。
SQL クエリ
SELECT 列リスト FROM テーブル A
UNION
SELECT 列リスト FROM テーブル B;3 つ以上の SELECT を縦に合成することもでき、その場合は演算子を縦に並べていきます。
ステップごとの動作
下の図は 2 つの顧客リストを集合演算で合成するイメージです。
代表例 1 — 2 店舗の顧客を統合
SQL クエリ
SELECT name FROM tokyo_customers
UNION
SELECT name FROM osaka_customers
ORDER BY name;両店舗を合わせた顧客名簿を、重複を取り除いて並べます。CRM ツールへのインポート用リストや、合計顧客数の算出に使います。
代表例 2 — 共通顧客と片方だけの顧客
SQL クエリ
-- 両店舗に存在する顧客
SELECT name FROM tokyo_customers
INTERSECT
SELECT name FROM osaka_customers;
-- 東京にいるが大阪にいない顧客
SELECT name FROM tokyo_customers
EXCEPT
SELECT name FROM osaka_customers;顧客重複や脱落を発見するのに重宝します。データ移行前の整合性チェックや、A/B テストの対象比較でもよく使います。
UNION と UNION ALL の違い
UNIONは重複を消すために内部でソートが走り、UNION ALLより遅くなりがちです。重複が無いと分かっているならUNION ALLを使ったほうがパフォーマンスは良いです。逆に、重複を確実に消したい場合はUNIONを選びましょう。同じデータが 100 万行 × 2 で並ぶような統計ジョブでは、UNION ALL を選ぶだけで体感速度が大きく変わることもあります。
列の型・順序ルール
集合演算では「すべての SELECT が同じ列数 + 同じ列の型」を持つ必要があります。型が違うと暗黙の型変換が走り、エラーになるか想定外の結果になります。列名は最初の SELECT のものが採用されるので、AS で揃えておくと安心です。レビューでは「列の順序と型が揃っているか」を最初に見ると、バグを発見しやすくなります。
ORDER BY の位置
集合演算した結果に対して並び替えたい場合は、最後の SELECT の後にまとめて 1 つだけ ORDER BY を書きます。個別の SELECT に ORDER BY を書いてもパース時にエラーになるか、無視されることがあります。
集合演算と JOIN の使い分け
- 横に増やしたい (列を追加) → JOIN
- 縦に増やしたい (行を追加) → UNION / UNION ALL
- 共通行だけ欲しい → INTERSECT (または JOIN + DISTINCT)
- 差分だけ欲しい → EXCEPT (または LEFT JOIN + IS NULL)
後者 2 つは JOIN で書ける場合もありますが、集合演算で書いたほうが意図が明確になることが多いです。コードレビューで意図が伝わる SQL を書けるかが、中級者と上級者の分かれ目になります。
集合演算でよくあるユースケース
- 複数店舗のログを 1 つにまとめる
- 旧テーブルと新テーブルの差分を確認する
- A/B テストの両グループに重複参加した人を特定する
- システム移行前後のデータを比較する
どれも「縦にデータを足し算 / 引き算する」操作です。横方向に列を増やす JOIN とは違う発想なので、両方を使い分けられるようになると SQL の引き出しが一気に増えます。
まとめ
UNIONは縦合成 + 重複除去、UNION ALLは重複も残すINTERSECTは共通行、EXCEPTは差分- 列数と列の型は完全に一致させる
- 並び順は最後にまとめて ORDER BY で指定する
- パフォーマンス重視なら可能な限り UNION ALL を選ぶ
- 「縦に増やすか横に増やすか」で JOIN と使い分ける
次のレッスン
次は 結合の実践演習 です。複数のテーブルを結合する実践的な演習です。JOIN句を使いこなしましょう。
事前確認 — 進む前に次の 3 つができることを確認しましょう。
- 集合演算 の要点を自分の言葉で説明できる
- このレッスンの最小コード (または操作手順) を見ずに書ける
- 練習問題やクイズで間違えた箇所を読み直して理解した
理解度チェック (30 秒)
Q. 集合演算 とは何か、1 文で説明してください。
A. 本文の「このレッスンで分かること」または冒頭の説明文を見直し、自分の言葉で要約できれば OK。詰まったら本レッスンの最初の H2 セクションを読み返してみましょう。
関連レッスン
テーブル構造
schema.sql
CREATE TABLE tokyo_customers (
id INT,
name VARCHAR(30)
);
CREATE TABLE osaka_customers (
id INT,
name VARCHAR(30)
);
INSERT INTO tokyo_customers VALUES
(1, '田中'),
(2, '鈴木'),
(3, '佐藤'),
(4, '高橋');
INSERT INTO osaka_customers VALUES
(5, '佐藤'),
(6, '鈴木'),
(7, '山田'),
(8, '伊藤');期待される出力
| customer_name |
|---|
| 伊藤 |
| 佐藤 |
| 山田 |
| 田中 |
| 鈴木 |
| 高橋 |