結果集合を組み合わせる - UNION / INTERSECT / EXCEPT
複数の SELECT 結果を縦に合成する集合演算。和・積・差と、重複排除の有無を押さえる
概念図
構文
sql
SELECT ... UNION [ALL] SELECT ...サンプル
顧客と見込み客のメール一覧を重複排除して合体させる
sql
SELECT email FROM customers
UNION
SELECT email FROM prospects;集合演算とは
集合演算は 複数の SELECT 結果を縦に合成する演算子です。JOIN が横に列を増やすのに対し、これらは行を増減させます。
- UNION: 和集合(A または B)
- INTERSECT: 積集合(A かつ B)
- EXCEPT (MySQL では
MINUS相当なし): 差集合(A から B を除く)
いずれも各 SELECT の列数と列の型が揃っている必要があります。列名は最初の SELECT のものが採用されます。
UNION と UNION ALL の違い
最も重要な違いは重複排除の有無です。
- UNION: 結果から重複行を排除する(内部的に DISTINCT / ソートが走るためコストが高い)
- UNION ALL: 重複を残したまま連結する。高速。重複がありえない、または残してよい場合はこちらを使う
INTERSECT / EXCEPT にも ALL バリアントがあり、INTERSECT ALL / EXCEPT ALL は重複を保持した多重集合演算になります(対応 RDBMS は限定的)。
sql
-- 性能差が出やすいパターン
SELECT id FROM orders_2025
UNION ALL -- 重複が無いと分かっているので ALL で十分
SELECT id FROM orders_2026;INTERSECT と EXCEPT
INTERSECT は両方の結果に現れる行だけを返します。EXCEPT は左の結果から右の結果にある行を除きます。NOT EXISTS / INNER JOIN で書き換えられることも多いですが、複数列の組を集合として比較したいときは集合演算のほうが自然です。
sql
-- 両方のキャンペーンに応募した人
SELECT user_id FROM campaign_a
INTERSECT
SELECT user_id FROM campaign_b;
-- A に応募して B には応募していない人
SELECT user_id FROM campaign_a
EXCEPT
SELECT user_id FROM campaign_b;ORDER BY と LIMIT の位置
個別の SELECT に ORDER BY を書いても無視されることがあります。全体のソートは最後の SELECT の後ろに ORDER BY を書くのが正解です。LIMIT も同様です。必要なら各 SELECT をカッコで囲って部分 LIMIT もできます(対応は RDBMS 依存)。
sql
SELECT id, name FROM a
UNION ALL
SELECT id, name FROM b
ORDER BY name
LIMIT 100;RDBMS 対応状況と注意
- UNION / UNION ALL: すべての主要 RDBMS でサポート
- INTERSECT / EXCEPT: PostgreSQL / SQLite / SQL Server / Oracle(
MINUS) で使える。MySQL は 8.0.31 以降で INTERSECT/EXCEPT をサポート。それ以前のバージョンではINNER JOINやNOT EXISTSで書き換える必要がある - 列数不一致や型不一致はエラー。NULL を含めて列の並びを揃える
- UNION は暗黙のソートが入るため、巨大データでは UNION ALL + 別途 DISTINCT のほうが速いこともある
