集合演算 UNION/INTERSECT/EXCEPT
年ごとに分かれた 2 つの注文テーブルを、1 つの表として扱いたい。JOIN はテーブルを横に伸ばす道具なので、この用途には使えません。縦に積むのは集合演算の仕事です。
2 つの SELECT を縦に積む
SQL クエリ
-- 今年と昨年の注文を 1 つの表として扱う
SELECT order_date, amount FROM orders_2025
UNION ALL
SELECT order_date, amount FROM orders_2024;SELECT 文を演算子でつなぐと、下の結果が上の結果の続きの行として並びます。条件は 2 つあります。列の数が同じであること、そして並び順に対応する列の型が同じであることです。列名は最初の SELECT のものが採用されるので、AS で読みやすい名前を付けておくと親切です。
並べ替えは、全部つなぎ終わった最後に 1 つだけ書きます。
SQL クエリ
SELECT order_date, amount FROM orders_2025
UNION ALL
SELECT order_date, amount FROM orders_2024
ORDER BY order_date DESC;UNION は重複を消す。そのぶん遅い
UNION と UNION ALL の違いは、同じ内容の行が 2 つ現れたときに 1 つにまとめるかどうか、それだけです。
UNION は重複を消すために、内部で全行を突き合わせる処理が走ります。行数が増えるほどこの分が効いてきて、数百万行どうしになると体感で分かるほど差が出ます。UNION ALL は上から順に積むだけなので、その処理がありません。
重複が出ないと分かっているなら UNION ALL を選びます。年で分かれた注文テーブルのように、同じ行が両方に現れない作りなら UNION を使う理由はありません。逆に、複数の名簿をまとめて「延べ人数ではなく実人数」を知りたいときは UNION です。
共通部分と差分を取る演算子もある
SQL クエリ
-- 両方の名簿に載っている人
SELECT email FROM newsletter_members
INTERSECT
SELECT email FROM campaign_entries;INTERSECT は両方に現れる行だけ、EXCEPT は左にあって右に無い行だけを返します。ただしこの 2 つは、使えるデータベースとそうでないものがあります。PostgreSQL では古くから使えますが、MySQL では比較的新しいバージョンで追加されたものなので、動かない環境もあります。
手元で弾かれたら、INNER JOIN や、LEFT JOIN と IS NULL の組み合わせで同じ結果を作れます。
横に列を増やしたいなら
JOIN、縦に行を増やしたいならUNION。迷ったら、増えるのは列なのか行なのかで決めます。
テーブル構造
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 |
|---|
| 伊藤 |
| 佐藤 |
| 山田 |
| 田中 |
| 鈴木 |
| 高橋 |