SQL UNIONの注意点
こんにちは!このページでは、SQLの集合演算の一つ「UNION」の重複の扱いを整理します。
試した環境
- PostgreSQL 17.5
SQLの集合演算とは?
SQLの「集合演算」は、複数の SELECT 文の結果を組み合わせて、新たな結果セットを作成するための便利な機能です。
よく使われる演算子には、UNION、 INTERSECT、EXCEPT (Oracleでは MINUS)などがあります。
これらの集合演算は直感的に理解しやすいため、「なんとなくのイメージで使っている」という方も少なくないかもしれません。
しかし、実際の動作がイメージと異なる場合もあり、意図しない結果につながることがあります。
そこで今回は、UNION に焦点を当て、具体的なデータを使いながら、その仕組みや注意点を解説していきます。
UNIONとは?
UNION は、複数の SELECT 文の結果を結合し、1つの結果セットとして返す集合演算です。
最大の特徴は、重複行を自動的に排除する点にあります。
UNIONのイメージ
「A + B − 重複データ = 結果」という式で表せるような関係です。これをイメージすると、以下のような図になります。
一見すると「A と B の重複部分 を取り除いてくれる」といったイメージですが、UNION の結果は、すべて一意な行(重複のない行)で構成されます。
つまり、A と B に共通する行がある場合だけでなく、A または B の中に同じデータが複数存在していた場合でも、それらの重複はすべて取り除かれ、1 行にまとめられる点に注意が必要です。
例えば、A と B に共通する行が存在しないクエリ同士を UNION した場合、どのような結果になるのかを確認してみましょう。
UNIONの結果を表示
-----------------------------
--purchase テーブル(A)
-----------------------------
SELECT product_name FROM purchase
;
product_name
--------------
apple
apple
(2 rows)
-----------------------------
--purchase_history テーブル(B)
-----------------------------
SELECT product_name FROM purchase_history
;
product_name
--------------
banana
(1 row)
-----------------------------
--union
-----------------------------
SELECT product_name FROM purchase
union
SELECT product_name FROM purchase_history
;
product_name
--------------
banana
apple
(2 rows)
実行結果は2行でした。テーブル間で共通する行はありませんが、purchaseテーブル内に重複するデータが含まれていたため、単純合計の3行ではなく、重複を除いた2行のみが抽出されています。UNION は「重複を除外して結果を返す」処理であると理解していても、この点があいまいになっている方も意外と多いのではないでしょうか。
UNION ALLとは?
UNION ALL は、複数の SELECT 文の結果を結合し、1つの結果セットとして返す集合演算です。UNION と異なり、重複行を排除せず、すべての行をそのまま返します。
UNION ALLのイメージ
UNION ALL は、「A + B = 結果」という単純な加算のイメージで捉えることができます。
この関係を視覚的に表すと、以下のような図になります。
A と B のデータをそのまま結合して返すため、UNION ALL の動作はイメージと実際の結果に大きなギャップがなく、直感的に理解しやすいといえるでしょう。
UNION ALLの結果を表示
-----------------------------
--purchase テーブル(A)
-----------------------------
SELECT product_name FROM purchase
;
product_name
--------------
apple
apple
banana
(3 rows)
-----------------------------
--purchase_history テーブル(B)
-----------------------------
SELECT product_name FROM purchase_history
;
product_name
--------------
banana
(1 row)
-----------------------------
--union all
-----------------------------
SELECT product_name FROM purchase
union all
SELECT product_name FROM purchase_history
;
product_name
--------------
apple
apple
banana
banana
(4 rows)
実行結果は4行でした。それぞれのクエリの合計(3行 + 1行)がそのまま抽出されました。
📌 今回のまとめ
UNION は、クエリの実行結果から重複する行を取り除いて返します。ここで重要なのは、「実行結果」に対して重複排除が行われる点です。これに対し、UNION ALL は重複を排除せず、すべての行をそのまま返すため、UNION よりも高速に処理される傾向があります。
そのほかの集合演算子はこちらで紹介しています