集合演算 — UNION / INTERSECT / EXCEPT

SQLの集合演算(UNION・INTERSECT・EXCEPT)で、複数のクエリ結果を縦につなぐ・共通部分を取る・差を取る方法を解説します。UNIONとUNION ALLの違いも整理します。

JOIN が複数のテーブルを「横」につなぐのに対し、集合演算は複数のクエリ結果を「縦」に組み合わせます。UNIONINTERSECTEXCEPT の 3 つを使うと、結果セットどうしを集合として足したり、共通部分や差を取ったりできます。

UNION — 結果を縦につなぐ

UNION は 2 つのクエリ結果を縦に結合します。たとえば「開発部の社員」と「給料 600 以上の社員」を 1 つのリストにまとめる場合です。

query.sql
SELECT name FROM employees WHERE dept = 'Dev'
UNION
SELECT name FROM employees WHERE salary >= 600;

集合演算を使うには、上下のクエリで列の数と型が揃っている必要があります。この例はどちらも name の 1 列なので結合できます。

UNION と UNION ALL の違い

UNION は結合した結果から重複を取り除きます。上の例で、開発部かつ給料 600 以上の人は両方の条件に該当しますが、UNION なら 1 回だけ表示されます。

重複を取り除かず、すべての行をそのまま連結したいときは UNION ALL を使います。

query.sql
SELECT name FROM employees WHERE dept = 'Dev'
UNION ALL
SELECT name FROM employees WHERE salary >= 600;

UNION は重複除去のために内部でソート・比較を行うため、UNION ALL より処理が重くなりがちです。重複が出ないと分かっている場合や、重複を残したい場合は UNION ALL を選ぶと効率的です。

INTERSECT — 共通部分

INTERSECT は 2 つの結果の両方に含まれる行だけを返します。「開発部で、かつ給料 600 以上の社員」を集合として求める例です。

query.sql
SELECT name FROM employees WHERE dept = 'Dev'
INTERSECT
SELECT name FROM employees WHERE salary >= 600;

同じことは WHERE dept = 'Dev' AND salary >= 600 でも書けますが、それぞれの条件が別テーブルや複雑なクエリになる場合、INTERSECT で「共通部分」として表現したほうが分かりやすいことがあります。

EXCEPT — 差

EXCEPT は「上のクエリにあって、下のクエリにない行」を返します。「開発部だが、給料 600 以上ではない社員」を求める例です。

query.sql
SELECT name FROM employees WHERE dept = 'Dev'
EXCEPT
SELECT name FROM employees WHERE salary >= 600;

集合の引き算だと考えると分かりやすいです。データベースによっては EXCEPTMINUS と呼ぶものもあります(Oracle など)。

並べ替えは最後にまとめて

集合演算の結果全体を並べ替えたいときは、ORDER BY一番最後に 1 つだけ書きます。

query.sql
SELECT name, salary FROM employees WHERE dept = 'Dev'
UNION
SELECT name, salary FROM employees WHERE salary >= 600
ORDER BY salary DESC;

各クエリの途中に ORDER BY は書けません(結合後の全体に対して並べ替えるため)。

まとめ

  • 集合演算は複数のクエリ結果を「縦」に組み合わせる(列数・型を揃える)
  • UNION は重複を除く、UNION ALL は重複を残す(こちらのほうが速い)
  • INTERSECT は共通部分、EXCEPT は差
  • ORDER BY は結合後の全体に対して最後に 1 つ

JOIN(横のつながり)と集合演算(縦の組み合わせ)を使い分けられると、データの取り出し方の引き出しが増えます。

手を動かして確認しましょう「サブクエリ」の演習問題で、実際に SQL を書いて自動採点できます。
問題一覧へ →