はじめに
UNIONは複数のSELECT結果を縦に結合する構文です。似た構文のUNION ALLとの違いや、他の集合演算子(INTERSECT, EXCEPT)も含めて整理します。
この後の流れ
- UNIONの基本
- UNIONとUNION ALLの違い(重要)
- UNIONを使うための3条件
- 応用:異なるテーブルの統合レポート
- INTERSECT/EXCEPTとの比較
- まとめ
1. UNIONの基本
複数のSELECT結果を縦方向に結合し、1つの結果セットにします。
SELECT customer_name, email FROM customers_tokyo
UNION
SELECT customer_name, email FROM customers_osaka;
2. UNIONとUNION ALLの違い(重要)
| 項目 | UNION | UNION ALL |
|---|---|---|
| 重複行 | 自動的に除去する | 除去せずそのまま結合 |
| 内部処理 | 重複排除のためソート/ハッシュ処理が走る | 単純結合のみ |
| パフォーマンス | 重複排除のコストがかかり遅い | 高速 |
-- 重複を許容してよい・パフォーマンス重視なら基本UNION ALL
SELECT product_id FROM sales_2025
UNION ALL
SELECT product_id FROM sales_2026;
実務での鉄則: 重複除去が明確に必要な場合以外は、原則
UNION ALLを使うべきです。「なんとなくUNION」を使うと、意図しない重複排除コストと、意図しないデータ欠落(本来重複ではない行が誤って同一視される等)のリスクがあります。
3. UNIONを使うための3条件
| 条件 | 内容 |
|---|---|
| ① 列数の一致 | 各SELECTの列数が同じであること |
| ② データ型の互換性 | 対応する列同士のデータ型に互換性があること |
| ③ 列名は最初のSELECTに従う | 出力の列名は1つ目のSELECT文の列名が採用される |
-- OK例:型は違っても暗黙変換できる範囲ならOK
SELECT employee_id, hire_date::text AS date_info FROM employees
UNION ALL
SELECT order_id, memo FROM orders;
4. 応用:異なるテーブルの統合レポート
ケース: 「顧客」と「見込み客」という別テーブルを、種別ラベル付きで1つの連絡先リストにまとめたい。
SELECT
name,
email,
'既存顧客' AS contact_type
FROM customers
UNION ALL
SELECT
name,
email,
'見込み客' AS contact_type
FROM leads
ORDER BY contact_type, name;
固定文字列リテラル('既存顧客')を列として混ぜることで、統合後もデータの出所を区別できます。ORDER BYはUNION全体の最後に一度だけ書きます(各SELECTには付けられません)。
5. INTERSECT/EXCEPTとの比較
UNION以外にも集合演算子があります。
| 演算子 | 意味 | イメージ(集合論) |
|---|---|---|
| UNION | 両方の和集合(重複除去) | A ∪ B |
| UNION ALL | 両方の和(重複含む) | A + B(多重集合) |
| INTERSECT | 両方に共通する行のみ | A ∩ B |
| EXCEPT | Aに存在してBに存在しない行 | A − B |
-- 今月と先月の両方で購入した顧客(共通)
SELECT customer_id FROM orders_this_month
INTERSECT
SELECT customer_id FROM orders_last_month;
-- 今月だけ購入して先月は購入していない顧客(新規/離脱把握)
SELECT customer_id FROM orders_this_month
EXCEPT
SELECT customer_id FROM orders_last_month;
注意: PostgreSQLはINTERSECT/EXCEPTをサポートしますが、MySQLは8.0.31以降でINTERSECT/EXCEPTに対応(それ以前はサブクエリ/EXISTSで代替が必要)という違いがあるため、DB移行時は要確認です。
6. まとめ
| 目的 | 使う構文 |
|---|---|
| 重複を除いて縦結合したい | UNION |
| 高速に縦結合したい(重複OK) | UNION ALL |
| 両方に共通する行だけ欲しい | INTERSECT |
| 片方にしかない行だけ欲しい | EXCEPT |
さいごに
UNION系はパフォーマンス影響が大きい構文なので、「本当に重複排除が必要か」を都度考える癖をつけると良いクエリが書けます。次回はPostgreSQLのストアドプロシージャです。