ユニオン演算子とは?データを統合する便利な演算子を徹底解説

データベースやプログラミングをしていると、「この2つのデータをまとめたい」という場面に出会います。

そんなとき活躍するのが、ユニオン演算子(Union Operator)です。

ユニオンは英語で「結合」「統合」という意味で、複数のデータセットを一つに統合する演算子なんです。

最も有名なのは、SQLのUNION演算子ですが、実は集合論、プログラミング言語の型システム、XPathなど、様々な場面で使われています。

この記事では、ユニオン演算子の基本から、SQLでの実践的な使い方、注意点、他の文脈での使用例まで、初心者の方にも分かりやすく解説していきます。

データベースを扱う方、プログラミングを学んでいる方は、ぜひ最後まで読んでみてください!

スポンサーリンク

集合としてのユニオン:基本概念

まず、ユニオンの基本的な考え方を理解しましょう。

集合の和(ユニオン)

数学の集合論では、ユニオンは和集合を表します。

2つの集合A、Bのユニオンは、「AまたはBに含まれる要素すべて」の集合です。

記号:

A ∪ B

具体例:

集合A = {1, 2, 3}
集合B = {3, 4, 5}

A ∪ B = {1, 2, 3, 4, 5}

重複する要素(この場合は3)は、結果には1回だけ含まれます。

ベン図で理解する

ベン図で表すと、分かりやすいですね。

2つの円(集合AとB)が重なっている図を想像してください。

ユニオン(A ∪ B)は、両方の円全体を表します。

一方、交差部分だけは「インターセクション(積集合)」です。

データベースでの応用

この集合の考え方が、データベースのUNION演算子の基礎になっています。

複数のクエリ結果を「統合」する、というのがユニオンの役割なんですね。

SQLのUNION演算子:基本

データベースで最もよく使われるユニオン演算子を見ていきましょう。

UNIONの基本文法

SQLでは、UNIONキーワードを使って、複数のSELECT文の結果を統合します。

基本的な構文:

SELECT 列1, 列2 FROM テーブル1
UNION
SELECT 列1, 列2 FROM テーブル2;

シンプルな例

実際の例を見てみましょう。

テーブル1(2023年の顧客):

顧客ID | 名前
-------|------
1      | 山田太郎
2      | 佐藤花子

テーブル2(2024年の顧客):

顧客ID | 名前
-------|------
3      | 鈴木一郎
4      | 田中美咲

UNIONクエリ:

SELECT 顧客ID, 名前 FROM 顧客2023
UNION
SELECT 顧客ID, 名前 FROM 顧客2024;

結果:

顧客ID | 名前
-------|--------
1      | 山田太郎
2      | 佐藤花子
3      | 鈴木一郎
4      | 田中美咲

2つのテーブルの結果が、1つにまとまりましたね!

UNIONの重要なルール

UNIONを使うときは、いくつかのルールがあります。

1. 列の数が同じ:
すべてのSELECT文で、同じ数の列を選択する必要があります。

2. 列のデータ型が互換:
対応する列のデータ型は、互換性がなければなりません。

3. 列名は最初のSELECTから:
結果セットの列名は、最初のSELECT文から取られます。

4. ORDER BYは最後に:
並べ替えは、UNION全体の最後に1回だけ指定します。

UNIONとUNION ALLの違い

UNIONには、2つのバリエーションがあります。

UNION(重複除去)

通常のUNIONは、重複する行を自動的に削除します。

例:

-- テーブルAに山田太郎がいる
-- テーブルBにも山田太郎がいる

SELECT 名前 FROM テーブルA
UNION
SELECT 名前 FROM テーブルB;

結果:
山田太郎は1回だけ表示されます。

UNION ALL(重複を残す)

UNION ALLは、重複をそのまま残します。

SELECT 名前 FROM テーブルA
UNION ALL
SELECT 名前 FROM テーブルB;

結果:
山田太郎が2回表示されます。

どちらを使うべきか

UNIONを使う場合:

  • 重複を排除したい
  • 結果をユニークにしたい
  • データの整合性が重要

UNION ALLを使う場合:

  • 重複も含めてすべてのデータが必要
  • パフォーマンスを重視(重複チェックがないため速い)
  • 重複がないことが分かっている

パフォーマンスの違い:
UNION ALLの方が速いです。
UNIONは重複チェックのため、内部的にソートやグループ化を行うからです。

実践的なUNIONの使用例

実際にどんな場面でUNIONが役立つのか見てみましょう。

例1:複数年のデータを統合

年度ごとに分かれたテーブルを統合します。

SELECT 注文ID, 商品名, 金額, '2022' AS 年度
FROM 注文2022
UNION ALL
SELECT 注文ID, 商品名, 金額, '2023' AS 年度
FROM 注文2023
UNION ALL
SELECT 注文ID, 商品名, 金額, '2024' AS 年度
FROM 注文2024
ORDER BY 年度, 注文ID;

複数年のデータを、一つのレポートにまとめられます。

例2:異なる条件の結果を統合

複雑な条件を、分けて書いて統合できます。

-- VIP顧客(購入額10万円以上)
SELECT 顧客ID, 名前, 'VIP' AS ランク
FROM 顧客
WHERE 総購入額 >= 100000

UNION

-- 常連顧客(購入回数20回以上)
SELECT 顧客ID, 名前, '常連' AS ランク
FROM 顧客
WHERE 購入回数 >= 20

UNION

-- 新規顧客
SELECT 顧客ID, 名前, '新規' AS ランク
FROM 顧客
WHERE 登録日 >= CURRENT_DATE - INTERVAL '30 days';

例3:マスターデータと履歴データの統合

現在のデータと過去のデータを一緒に表示します。

-- 現在の従業員
SELECT 従業員ID, 名前, 部署, '在職中' AS 状態
FROM 従業員

UNION ALL

-- 退職済み従業員
SELECT 従業員ID, 名前, 部署, '退職済み' AS 状態
FROM 退職従業員
ORDER BY 従業員ID;

例4:集計結果の統合

異なる集計結果を一つのレポートにまとめます。

-- 商品カテゴリ別の売上
SELECT カテゴリ名 AS 区分, SUM(売上) AS 金額
FROM 売上明細
JOIN 商品 ON 売上明細.商品ID = 商品.商品ID
GROUP BY カテゴリ名

UNION ALL

-- 総合計行を追加
SELECT '総合計' AS 区分, SUM(売上) AS 金額
FROM 売上明細;

UNIONと他の集合演算子

SQLには、UNION以外の集合演算子もあります。

INTERSECT(積集合)

2つのクエリ結果の共通部分を取得します。

SELECT 顧客ID FROM 購入履歴2023
INTERSECT
SELECT 顧客ID FROM 購入履歴2024;

「2023年も2024年も購入した顧客」だけが取得できます。

EXCEPT(差集合)

最初のクエリ結果から、2番目のクエリ結果を除外します。

SELECT 顧客ID FROM 全顧客
EXCEPT
SELECT 顧客ID FROM 購入履歴2024;

「2024年に購入していない顧客」が取得できます。

注意:
データベースによっては、MINUSという名前の場合があります(Oracleなど)。

3つの演算子の比較

集合A = {1, 2, 3, 4}
集合B = {3, 4, 5, 6}

  • A UNION B = {1, 2, 3, 4, 5, 6}(すべて)
  • A INTERSECT B = {3, 4}(共通部分)
  • A EXCEPT B = {1, 2}(Aだけにあるもの)

UNIONのパフォーマンス最適化

UNIONを効率的に使うためのコツです。

UNION ALLを優先する

重複がない、または重複が問題にならない場合は、UNION ALLを使いましょう。

重複チェックが不要なため、大幅に高速化できます。

インデックスの活用

各SELECT文で使用される列に、適切なインデックスを設定しましょう。

UNIONの前に、個々のクエリが高速である必要があります。

不要な列を選択しない

必要な列だけを選択することで、処理量を減らせます。

-- 良くない例
SELECT * FROM テーブル1
UNION
SELECT * FROM テーブル2;

-- 良い例
SELECT ID, 名前 FROM テーブル1
UNION
SELECT ID, 名前 FROM テーブル2;

サブクエリの最適化

UNIONする各SELECT文を、できるだけシンプルにしましょう。

複雑なサブクエリや結合は、パフォーマンスを低下させます。

件数制限の考慮

各クエリにLIMIT/TOP句を使って、結果数を制限することも検討しましょう。

SELECT 商品名, 売上 FROM 商品A ORDER BY 売上 DESC LIMIT 10
UNION ALL
SELECT 商品名, 売上 FROM 商品B ORDER BY 売上 DESC LIMIT 10
ORDER BY 売上 DESC
LIMIT 10;

UNIONの注意点とよくあるエラー

UNIONを使うときの、典型的な問題と対処法です。

エラー1:列数が一致しない

-- エラー!
SELECT 列1, 列2 FROM テーブル1
UNION
SELECT 列1 FROM テーブル2;

対処法:
すべてのSELECT文で、同じ数の列を選択します。

-- 正しい
SELECT 列1, 列2 FROM テーブル1
UNION
SELECT 列1, NULL AS 列2 FROM テーブル2;

エラー2:データ型が互換性がない

-- エラーの可能性
SELECT 顧客ID, 名前 FROM 顧客
UNION
SELECT 注文ID, 注文日 FROM 注文;

名前(文字列)と注文日(日付)は互換性がありません。

対処法:
データ型を変換するか、列の対応を見直します。

エラー3:ORDER BYの位置が間違っている

-- エラー!
SELECT 名前 FROM テーブル1 ORDER BY 名前
UNION
SELECT 名前 FROM テーブル2 ORDER BY 名前;

対処法:
ORDER BYは、最後に1回だけ書きます。

-- 正しい
SELECT 名前 FROM テーブル1
UNION
SELECT 名前 FROM テーブル2
ORDER BY 名前;

注意点:NULLの扱い

UNIONでは、NULLも重複として扱われます。

SELECT NULL AS 値
UNION
SELECT NULL AS 値;

-- 結果:1行だけ(NULL)

UNION ALLなら、2行のNULLが返ります。

プログラミング言語のユニオン型

データベース以外でも、ユニオンは使われています。

TypeScriptのユニオン型

TypeScriptでは、複数の型のいずれかを表現できます。

// 文字列または数値
type StringOrNumber = string | number;

let value: StringOrNumber;
value = "Hello";  // OK
value = 42;       // OK
value = true;     // エラー!

縦棒(|)がユニオン演算子です。

Pythonの型ヒント

Python 3.10以降では、同様の記法が使えます。

from typing import Union

# 文字列または整数
def process(value: Union[str, int]) -> str:
    return str(value)

# Python 3.10以降
def process(value: str | int) -> str:
    return str(value)

Rustのenum

Rustでは、enumが型のユニオンとして機能します。

enum Result<T, E> {
    Ok(T),
    Err(E),
}

ResultはTまたはEのどちらかを持つ、という表現です。

XPathのユニオン演算子

XML処理でもユニオンが使われます。

XPathでの使用

XPathでは、パイプ(|)がユニオン演算子です。

//book/title | //book/author

これは、「titleまたはauthor要素」を選択します。

具体例

<books>
  <book>
    <title>星の王子さま</title>
    <author>サン=テグジュペリ</author>
  </book>
</books>

上記のXPathは、titleとauthorの両方を取得します。

実践的なSQLクエリ例

より複雑な実例を見てみましょう。

売上レポートの生成

-- 日別売上
SELECT 
    DATE(注文日) AS 日付,
    '日次' AS 単位,
    SUM(金額) AS 売上
FROM 注文
WHERE 注文日 >= '2024-01-01'
GROUP BY DATE(注文日)

UNION ALL

-- 月別売上
SELECT 
    DATE_FORMAT(注文日, '%Y-%m-01') AS 日付,
    '月次' AS 単位,
    SUM(金額) AS 売上
FROM 注文
WHERE 注文日 >= '2024-01-01'
GROUP BY DATE_FORMAT(注文日, '%Y-%m')

ORDER BY 日付, 単位;

在庫状況の統合表示

-- 通常在庫
SELECT 
    商品ID,
    商品名,
    在庫数,
    '通常倉庫' AS 倉庫
FROM 通常在庫
WHERE 在庫数 > 0

UNION ALL

-- 予約在庫
SELECT 
    商品ID,
    商品名,
    在庫数,
    '予約倉庫' AS 倉庫
FROM 予約在庫
WHERE 在庫数 > 0

ORDER BY 商品ID, 倉庫;

UNIONの代替手段

状況によっては、他の方法が適切な場合もあります。

JOINとの比較

UNION:

  • 行を縦に結合(追加)
  • 異なるテーブルからデータを統合

JOIN:

  • 列を横に結合(拡張)
  • 関連するテーブルを結合

CASE文での代替

条件によって異なる値を返す場合、CASE文で代替できることがあります。

-- UNIONを使う場合
SELECT 名前, '男性' AS 性別 FROM 男性会員
UNION
SELECT 名前, '女性' AS 性別 FROM 女性会員;

-- CASE文で代替
SELECT 
    名前,
    CASE WHEN 性別コード = 1 THEN '男性' ELSE '女性' END AS 性別
FROM 全会員;

テーブルが分かれていない場合は、CASE文の方がシンプルです。

ビューの活用

頻繁に使うUNIONクエリは、ビューとして保存できます。

CREATE VIEW 全顧客 AS
SELECT 顧客ID, 名前, 'オンライン' AS 種別 FROM オンライン顧客
UNION
SELECT 顧客ID, 名前, '店舗' AS 種別 FROM 店舗顧客;

-- 使用時
SELECT * FROM 全顧客 WHERE 名前 LIKE '山田%';

データベースごとの違い

UNIONの挙動は、データベースによって少し異なります。

MySQL

  • UNION、UNION ALLをサポート
  • INTERSECT、EXCEPTは8.0.31以降でサポート
  • パフォーマンスは良好

PostgreSQL

  • すべての集合演算子をサポート
  • 標準SQLに準拠
  • 高度な最適化

SQL Server

  • UNION、UNION ALLをサポート
  • INTERSECT、EXCEPTをサポート
  • ENTERPRISEで高度な最適化

Oracle

  • UNION、UNION ALLをサポート
  • INTERSECT、MINUSをサポート(EXCEPTではない)
  • 独自の最適化機能

まとめ:ユニオン演算子でデータを効率的に統合

ユニオン演算子は、複数のデータセットを統合する強力なツールです。

この記事の重要ポイントをおさらいしましょう:

  • ユニオンは集合の和(統合)を表す概念
  • SQLではUNION演算子で複数のSELECT結果を統合
  • UNIONは重複を除去、UNION ALLは重複を残す
  • 列数とデータ型を揃える必要がある
  • パフォーマンス重視ならUNION ALLを使う
  • INTERSECT(積集合)やEXCEPT(差集合)も便利
  • ORDER BYは最後に1回だけ指定
  • TypeScriptやPythonでは型のユニオンとして使用
  • XPathではパイプ(|)がユニオン演算子
  • 適切なインデックスでパフォーマンスを最適化

ユニオン演算子は、データ分析、レポート生成、システム統合など、様々な場面で活躍します。

「複数のテーブルやクエリ結果を一つにまとめたい」

そんなとき、ユニオン演算子の出番です。

この知識が、あなたのデータベース操作やプログラミングに役立てば幸いです!

コメント

タイトルとURLをコピーしました