SQLのUNIONとは ― UNION ALL・JOINとの違い
UNIONは
SQLのUNIONとは、SELECTの結果を縦につなぐ演算子
SQLのUNIONは、2つのSELECT文の結果を縦につなぎ、1つの結果にする演算子です。売上が年ごとに別のテーブルに分かれていて、全部の年を1つの一覧で見たいときに使います。
気をつけたいのは、UNIONが重複した行を黙って消すことです。同じ商品が同じ金額で売れた行が1行に減るので、そのまま合計すると売上が足りなくなります。UNION ALLとの違い、JOINとの使い分け、列の数や型が合わないエラーの直し方も、同じ売上の例で説明します。
UNIONの書き方は、2つのSELECTの間にUNIONを置くだけ
売上を年ごとのテーブルに分けて持っている例で試します。売上2025テーブルは次の3行です。
| 注文番号 | 商品名 | 金額 |
|---|---|---|
| 1001 | マウス | 1000 |
| 1002 | キーボード | 2500 |
| 1003 | マウス | 1000 |
売上2026テーブルも同じ3列で、次の3行です。
| 注文番号 | 商品名 | 金額 |
|---|---|---|
| 2001 | マウス | 1000 |
| 2002 | キーボード | 2800 |
| 2003 | スピーカー | 2000 |
2つのSELECT文の間にUNIONを書くと、結果が1つになります。文の終わりのセミコロンは、最後に1つだけ書きます。
SELECT 商品名, 金額
FROM 売上2025
UNION
SELECT 商品名, 金額
FROM 売上2026;| 商品名 | 金額 |
|---|---|
| マウス | 1000 |
| キーボード | 2500 |
| キーボード | 2800 |
| スピーカー | 2000 |
2つのテーブルを合わせると6行あるのに、結果は4行です。マウス 1000の3行が、1行にまとまったからです。キーボードは金額が違うので、別の行として2行残ります。
3年分をつなぐときは、続けてUNIONとSELECT文を書きます。PostgreSQLの文書によると、複数のUNIONは左から順に処理します。
UNIONとUNION ALLの違いは、重複した行を消すかどうか
UNIONは重複した行を1行にまとめ、UNION ALLは全ての行をそのまま残します。同じ2つのテーブルで比べると、次のようになります。
| 比べる | UNION | UNION ALL |
|---|---|---|
| 重複した行 | 1行に | そのまま |
| 売上の例の行数 | 4行 | 6行 |
| 売上の例の合計 | 8300 | 10300 |
| 処理の手間 | 重複を | つなぐだけで |
売上の合計は、UNIONを使うと2000少なくなります。エラーは出ないので、数字が合わないことに気づくまで間違いが残ります。別々の注文を全て数える集計には、UNION ALLを使います。
SELECT SUM(金額) AS 合計
FROM (
SELECT 商品名, 金額
FROM 売上2025
UNION ALL
SELECT 商品名, 金額
FROM 売上2026
) AS 全売上;
-- 結果:10300FROMの中のSELECT文はサブクエリで、つないだ結果を1つの表として扱えます。SUMは集約関数なので、GROUP BYを足すと商品ごとの合計も出せます。
UNION ALLは、つないだ結果から重複を探す処理をしません。PostgreSQLの文書も、UNION ALLはふつうUNIONよりかなり速いと書き、使えるときはALLを使うよう勧めています。重複を消す必要があるときだけ、UNIONを選びます。
重複かどうかは、SELECTで選んだ全ての列の値で決まります。注文番号も選ぶと、どの行も値が違うので、UNIONでも6行全てが残ります。消えるのは2つの結果の間の重複だけではありません。売上2025の中にあるマウス 1000の2行も、1行になります。
NULLどうしは、UNIONでは同じ値として扱います。SELECT NULL UNION SELECT NULL の結果は1行です。NULLを=で比べても一致しないWHERE句とは、動きが違います。
演算を選んで、残る行と合計を比べる
次の図で売上の2つのテーブルをつなぐ演算を選ぶと、入力の行が残るかどうかと、結果の行数・合計が切り替わります。結果は、PostgreSQLで実際に実行して確かめたものです。
SELECT 商品名, 金額
FROM 売上2025
UNION ALL
SELECT 商品名, 金額
FROM 売上2026;- 1001マウス1000残る
- 1002キーボード2500残る
- 1003マウス1000残る
- 2001マウス1000残る
- 2002キーボード2800残る
- 2003スピーカー2000残る
- マウス1000
- キーボード2500
- マウス1000
- マウス1000
- キーボード2800
- スピーカー2000
全ての
SELECT 商品名, 金額
FROM 売上2025
UNION
SELECT 商品名, 金額
FROM 売上2026;- 1001マウス1000残る
- 1002キーボード2500残る
- 1003マウス1000重複で消える
- 2001マウス1000重複で消える
- 2002キーボード2800残る
- 2003スピーカー2000残る
- マウス10003行を1行に
- キーボード2500
- キーボード2800
- スピーカー2000
マウス 1000の
SELECT 注文番号, 商品名, 金額
FROM 売上2025
UNION
SELECT 注文番号, 商品名, 金額
FROM 売上2026;- 1001マウス1000残る
- 1002キーボード2500残る
- 1003マウス1000残る
- 2001マウス1000残る
- 2002キーボード2800残る
- 2003スピーカー2000残る
- 1001マウス1000
- 1002キーボード2500
- 1003マウス1000
- 2001マウス1000
- 2002キーボード2800
- 2003スピーカー2000
注文番号は
SELECT 商品名, 金額
FROM 売上2025
INTERSECT
SELECT 商品名, 金額
FROM 売上2026;- 1001マウス1000残る
- 1002キーボード2500結果に入らない
- 1003マウス1000重複で消える
- 2001マウス1000重複で消える
- 2002キーボード2800結果に入らない
- 2003スピーカー2000結果に入らない
- マウス10003行を1行に
両方の
SELECT 商品名, 金額
FROM 売上2025
EXCEPT
SELECT 商品名, 金額
FROM 売上2026;- 1001マウス1000結果に入らない
- 1002キーボード2500残る
- 1003マウス1000結果に入らない
- 2001マウス1000結果に入らない
- 2002キーボード2800結果に入らない
- 2003スピーカー2000結果に入らない
- キーボード2500
売上2026にも
SELECT 商品名, 金額
FROM 売上2026
EXCEPT
SELECT 商品名, 金額
FROM 売上2025;- 1001マウス1000結果に入らない
- 1002キーボード2500結果に入らない
- 1003マウス1000結果に入らない
- 2001マウス1000結果に入らない
- 2002キーボード2800残る
- 2003スピーカー2000残る
- キーボード2800
- スピーカー2000
2つの
結果の
UNIONでつなぐ列は、数と型をそろえる
つなぐSELECTどうしは、列の数が同じで、対応する列の型が互換でなければなりません。互換とは、1つの列にまとめられる型のことです。PostgreSQLでは整数の列と小数の列はつなげますが、整数の列と文字列の列をつなぐとエラーになります。
MySQLの文書は、対応する列を同じ型にするよう書いています。型が合わないときは、全てのSELECTの値を踏まえて結果の型と長さを決めるとも説明しています。製品によって動きが違うので、同じ型にそろえて書いておくと安全です。
列は名前ではなく、左から何番目かで対応します。名前が違っていてもつながり、結果の列名は最初のSELECTの列名になります。次の文の結果の列名は、商品名と売上額です。
SELECT 商品名, 金額 AS 売上額
FROM 売上2025
UNION ALL
SELECT 商品名, 金額
FROM 売上2026;列の順番を取り違えても、型が同じならエラーになりません。例えば、注文番号と金額はどちらも整数です。片方のSELECTだけ順番を逆に書くと、金額の列に注文番号が入ったまま結果が返ります。
片方のテーブルにしかない列は、もう片方のSELECTにNULLや固定の値を書いて、列の数をそろえます。固定の値の列は、どの年の行かを見分ける目印にも使えます。
SELECT 商品名, 金額, 2025 AS 年
FROM 売上2025
UNION ALL
SELECT 商品名, 金額, 2026
FROM 売上2026;JOINは横に、UNIONは縦につなぐ
UNIONは行を増やし、JOINは列を増やします。どちらも2つの表をまとめますが、できあがる表の形が違います。
同じ列を持つデータが複数のテーブルに分かれているなら、UNIONを使います。行に別のテーブルの情報を足したいなら、JOINを使います。売上の行に商品の分類を足す処理はJOINで、2025年と2026年の売上を1つの一覧にする処理はUNIONです。
| 比べる | UNION | JOIN |
|---|---|---|
| 増えるもの | 行 | 列 |
| つなぎ方 | 左から | ONの |
| 向いている場面 | 同じ形の | 行に |
2つの年の売上をJOINでつなぐと、2025年の列の右に2026年の列が並びます。全部の年を1つの列に縦に並べたいときは、UNIONを使います。JOINでも、相手の表に一致する行が複数あれば行は増えます。
INTERSECTは両方にある行、EXCEPTは片方だけにある行を返す
UNIONと同じ書き方で使える演算子が、ほかに2つあります。INTERSECTは両方の結果にある行を返します。EXCEPTは、先のSELECTの結果にあって、後のSELECTの結果に無い行を返します。
| 演算子 | 返す行 | 集合演算 | 売上の |
|---|---|---|---|
| UNION | どちらかの | 和 | 4行 |
| INTERSECT | 両方の | 積 | マウス 1000 |
| EXCEPT | 先の | 差 | キーボード 2500 |
EXCEPTは、書く順番で結果が変わります。演算を選ぶ図で「EXCEPT(順番を逆に)」を押すと、残る行が入れ替わることを確かめられます。PostgreSQLとMySQLでは、どの演算子も既定で重複を消し、ALLを付けると重複した行を残します。
MySQLでINTERSECTとEXCEPTを使えるのは、8.0.31以降です。MySQLの文書によると、Oracleなどの一部の製品はEXCEPTをMINUSと呼びます。MySQLではMINUSを使えず、PostgreSQLで実行しても構文エラーになりました。和・積・差という集合演算の考え方は、関係演算の記事で説明しています。
ORDER BYは文の最後に1回だけ書く
ORDER BYは文の最後に書き、つないだ結果の全体を並べ替えます。WHEREはSELECTごとに書き、そのSELECTの行だけを絞ります。次の文は、2000以上の売上を年に関係なく金額の高い順に並べます。
結果は、キーボード 2800、キーボード 2500、スピーカー 2000の3行です。ORDER BYを書かないと、行の順番は決まりません。1つ目のSELECTの行が先に並ぶとも限らないので、順番が必要なら必ず書きます。
年ごとに金額の最も高い1行を取りたいときは、途中のSELECTにもORDER BYが必要です。その場合は、SELECT文を括弧で囲みます。LIMIT 1は、先頭の1行だけを返す指定です。
(SELECT 商品名, 金額
FROM 売上2025
ORDER BY 金額 DESC LIMIT 1)
UNION ALL
(SELECT 商品名, 金額
FROM 売上2026
ORDER BY 金額 DESC LIMIT 1);
-- 結果:キーボード 2500
-- キーボード 2800MySQLの文書は、括弧の中のORDER BYが最終的な順番を決めるとは限らないと書いています。LIMITと組み合わせて行を選ぶために使い、全体の順番は最後のORDER BYで決めます。
UNIONのエラーは、列の数・型・ORDER BYの位置を確かめる
次の表のPostgreSQLのエラーの文は、実際に実行して出たものです。MySQLのエラーの文は、公式のエラー一覧から引きました。
| エラーの | 原因 | 直し方 |
|---|---|---|
each UNION query must have the same number of columns | PostgreSQLで、 | 少ない |
The used SELECT statements have a different number of columns | MySQLで、 | 上と |
UNION types text and integer cannot be matched | 対応する | 列の |
syntax error at or near "UNION" | 途中の | 最後に |
column "金額" does not exist | ORDER BYに、 | 最初の |
invalid UNION/INTERSECT/EXCEPT ORDER BY clause | ORDER BYに | 計算した |
文字列の列と整数の列をあえてつなぐときは、CAST(金額 AS text) のように型を変えて書きます。ORDER BYに書けるのは、PostgreSQLでは結果の列名か列番号だけです。MySQLでは列番号の指定は非推奨で、売上2025.金額 のような表名付きの列名も書けません。
参考にした資料
- PostgreSQL 18 文書「Combining Queries (UNION, INTERSECT, EXCEPT)」(3つの演算子、列の数と型の条件、括弧)
- PostgreSQL 18 文書「SELECT」(UNION ALLの速さ、ORDER BYに書けるもの)
- PostgreSQL 18 文書「UNION, CASE, and Related Constructs」(列の型の決まり方)
- MySQL 8.4 Reference Manual「Set Operations with UNION, INTERSECT, and EXCEPT」(列名、型、ORDER BYとLIMIT、MINUS)
- MySQL 8.0 Reference Manual「Set Operations with UNION, INTERSECT, and EXCEPT」(INTERSECTとEXCEPTを使えるバージョン)
- MySQL 8.4 Error Reference「Server Error Message Reference」(列の数が違うときのエラーの文)
練習問題(全5問)
正解:UNION ALL
UNIONは
正解:1行
UNIONは、
正解:エラーに
つなぐ
正解:JOIN
別の
正解:文全体の
括弧を
選んでから「採点する」を押すと、解説つきで答え合わせができます。
関連記事
おすすめの本 PR
セキュリティとデータベースは、基本情報技術者試験の対策本でまとめて学べます。

