Tracks
集合演算はSQLの基盤であり、複数のソースからデータを結合・比較・フィルタリングすることを可能にします。データ統合やクレンジングから高度な分析・レポーティングまで、幅広いタスクで欠かせません。
このチュートリアルでは、集合演算子とは何か、SQLでの使い方、実践的な活用方法などを学びます。完全なSQL学習リソースを探している場合は、7コースから成るSQL Fundamentalsスキルトラックをご覧ください。
お急ぎの方のために、まずはSQLにおける集合演算のごく短い説明から始めましょう。
SQLの集合演算とは?
SQLの集合演算は、2つ以上のSELECT文の結果を結合または比較するための手法です。数学の集合演算のように機能し、クエリで返された行の和集合・共通部分・差集合を求められます。これにより、複数のソースや視点からデータを分析する際に不可欠な手段となります。
主要な集合演算の概要は次のとおりです。
-
UNION:2つ以上のSELECT文から重複を排除して一意な行を結合します。 -
UNION ALL:2つ以上のSELECT文から重複を含めてすべての行を結合します。 -
INTERSECT:両方のSELECT文に出現する行のみを返します。 -
EXCEPT:最初のSELECT文にあり、2番目にはない行を返します。
関係代数の演算との比較
関係代数は、データベースクエリを理解するための理論的枠組みです。投影、選択、結合といった抽象的な演算を提供し、数学的原理に根差し、特定のデータベースシステムに依存しません。データベース操作を支える「舞台裏の」ロジックと考えることができます。
SQLの集合演算子は、これらの概念をデータベース環境で実践的に実装したものです。SQLクエリの結果集合に対して、和・共通部分・差といった集合演算を直接実行できます。
関係代数がデータベース操作の形式的基盤を提供する一方で、SQLの集合演算子はデータ操作を行うための標準化されたユーザーフレンドリーなインターフェースを提供します。
集合演算子と関係代数の関係を理解すると、SQLの理論的背景に対する洞察が得られます。この結び付きを知ることで、内部で何が起きているかをより明確にイメージしながらクエリを書けるようになります。
SQLの集合演算子の種類
SQLには主に3つの集合演算子があります。
-
UNION -
INTERSECT -
EXCEPT(一部の方言ではMINUS)
これらは数学的には、和集合・共通部分・差集合の概念に対応します。
集合演算子を使う際のルール
集合演算子でクエリを結合する前に、次の4つの要件を満たす必要があります。いずれかを間違えると、クエリはエラーになります。
-
同じ列数:各
SELECTは同じ列数を返さなければなりません。片方が3列、もう片方が2列だと、データベースはエラーを投げます。 -
互換性のあるデータ型:対応する列は型が一致している必要があります。同じ位置にある
VARCHARの名前列とINTEGERのID列を結合することはできません。 -
同じ列順:SQLは列名ではなく位置で列を対応付けます。最初のクエリの1列目は、列名に関係なく2つ目のクエリの1列目に対応します。
-
ORDER BYは最後に1回だけ:ORDER BYは結合したクエリの一番最後に1回だけ記述します。個々のSELECT文の中では使えません。
もう1点:出力の列名は最初のSELECT文から取られます。後続のクエリでのエイリアスは無視されるため、出力名をカスタマイズしたい場合は最初のクエリにのみエイリアスを付けてください。
UNION演算子の使い方
UNION演算子は、2つ以上のSELECTクエリの結果を1つの結果集合に結合し、デフォルトで重複行を削除します。
たとえば、employeesとcontractorsという2つのテーブルがあり、それぞれにcontractors、department、salaryのような同様の列があるとします。学習のため、次のようなダミーテーブルを考えます。
employees:
|
name |
department |
salary |
|
Alice |
Marketing |
65000 |
|
Bob |
Sales |
70000 |
|
Carol |
Engineering |
80000 |
|
John |
HR |
55000 |
contractors:
|
name |
department |
salary |
|
David |
Marketing |
60000 |
|
Eva |
Sales |
68000 |
|
Carol |
Engineering |
75000 |
次のコマンドで両テーブルの結果を結合できます。
-- Using UNION to combine all employees and contractors
SELECT name, department, salary FROM employees
UNION
SELECT name, department, salary FROM contractors;
このクエリは、employeesとcontractorsの両テーブルからname、department、salary列を選択し、1つの結果集合に結合します。UNION演算子は、最終的な結果集合から重複行を自動的に取り除きます。
|
name |
department |
salary |
|
Alice |
Marketing |
65000 |
|
Bob |
Sales |
70000 |
|
Carol |
Engineering |
80000 |
|
John |
HR |
55000 |
|
David |
Marketing |
60000 |
|
Eva |
Sales |
68000 |
両テーブルに登場するCarolは、結果では1回だけ表示されている点に注目してください。Carolの両方のレコード(給与が異なる)を保持したい場合は、UNION ALLを使います。
UNION と UNION ALL の違い
UNION演算子はNULL値を削除しません。ある結果集合の列にNULLがあり、対応する別の結果集合の列に非NULLの値がある場合でも、UNION演算子で作成される最終結果集合にはNULL値が保持されます。
結果集合にNULL値を含め、UNIONによる削除を避けたい場合は、代わりにUNION ALLを使用します。この演算子は、重複やNULL値を含むかどうかに関わらず、複数のSELECTクエリのすべての行を結合します。
INTERSECT演算子の使い方
INTERSECT演算子は、両方の結果集合に出現する行のみを返します。両方のグループに属する人を見つけるイメージです。
先ほどのテーブルに対してINTERSECTを使ってみましょう。例として、nameとdepartment列だけを照会します。
-- Using INTERSECT to find common employees
SELECT name, department FROM employees
INTERSECT
SELECT name, department FROM contractors;
このクエリは、employeesおよびcontractorsの両テーブルからnameとdepartment列を選択し、選択したすべての列に基づいて両方のテーブルに存在する行のみを返します。
|
name |
department |
|
Carol |
Engineering |
INTERSECT演算子は標準的な比較ルールに基づいてNULL値を扱い、対応する列を比較する際にNULL値を等しいと見なします。また、空の結果集合を扱う場合は空集合になります。
つまり、一方の結果集合にNULL値が存在し、もう一方の結果集合の対応する列に非NULL値がある場合、行は等しいと見なされず、共通部分の結果には含まれません。
さらに、INTERSECT演算子に渡される結果集合の一方が空である場合、全体の結果も空になります。空集合と他の集合には共通の行がないためです。
UNION演算子の使い方
EXCEPT演算子は、最初の結果集合にあって2番目にはない行を返します。
Oracle DatabaseではEXCEPTはMINUSと記述します。その他の主要な方言(PostgreSQL、SQL Server、MySQL 8.0.31+、SQLite)はEXCEPTを使用します。
たとえば、次のクエリを実行するとします。
-- Using EXCEPT to find employees who are not contractors
SELECT name, department, salary FROM employees
EXCEPT
SELECT name, department, salary FROM contractors;
employeesテーブルからname、department、salary列を選択し、contractorsテーブルに存在しない行のみを返します。
|
name |
department |
salary |
|
Alice |
Marketing |
65000 |
|
Bob |
Sales |
70000 |
|
John |
HR |
55000 |
EXCEPT演算子もNULL値の扱いについて標準的な比較ルールに従います。空の結果集合に対する挙動としては、最初の結果集合が空であれば結果は空になり、2番目の結果集合が空であれば最初の結果集合のすべての行が含まれます。
SQLの集合演算子:パフォーマンスと最適化
集合演算子がSQLのクエリ性能に与える影響は、関与するデータセットのサイズ、クエリの複雑さ、使用しているデータベース管理システム(DBMS)などの要因によって異なります。
ここでは、主要な要因と最適化戦略を整理します。
データ量とクエリの複雑さ
大量データを扱う場合、結合・共通部分・比較の対象となる結果集合が大きくなるため、集合演算子はクエリ性能に大きく影響します。処理に必要な時間が増加するためです。
複数のサブクエリ、結合、集合演算子を含む複雑なクエリは、追加の処理オーバーヘッドを生み、性能に影響する可能性があります。演算の連鎖や集合演算の入れ子は、パフォーマンスの悪化をさらに招く恐れがあります。
インデックスと最適化手法
集合演算に関わる列を適切にインデックス化すると、クエリ性能を大きく改善できます。インデックスにより、データベースエンジンは関連する行を素早く特定・取得でき、全表走査を減らし、実行時間を短縮します。
集合演算子を含むクエリの性能を高めるために、データベース管理者や開発者は、クエリの書き換え、実行計画の分析、スキーマ最適化といった手法を適用できます。クエリキャッシュやマテリアライズド・ビューのように、複雑なクエリの結果を事前計算・保存する手法も、集合演算の計算負荷を軽減するのに有効です。
データベースエンジンとハードウェア資源
集合演算の性能は、基盤となるデータベースエンジンとその最適化能力によっても変わります。DBMSごとに集合演算の処理に用いる最適化戦略やアルゴリズムが異なり、性能差が生じます。
CPU、メモリ、ディスクI/Oといったハードウェア資源の可用性も、集合演算を含むクエリの性能に影響します。十分なハードウェア資源があれば、ボトルネックの緩和や効率的な実行に役立ちます。
SQL集合演算子の実務での活用
集合演算子は理論上の道具にとどまりません。実務でも活躍し、意思決定に大きな影響を与えます。ここでは、企業が集合演算子を使って顧客基盤をセグメント化し、ターゲティングを行う簡略化した例を見ていきます。
シナリオ
オンラインと実店舗の両方で販売する企業を想像してください。次の2つのデータセットがあります。
- オンライン購入:オンライン購入者の顧客ID、購入履歴、属性、所在地。
- 店舗取引:来店購入者について同様の情報。
集合演算子の活用
すべての顧客の全体像を把握するには、まずUNIONで両データセットを1つのテーブルに結合し、重複を取り除きます。これにより、顧客基盤全体の統一ビューが得られます。
次に、INTERSECTを使って、オンラインと店舗の両方で購買した顧客を特定します。複数チャネルでブランドに高い関与を示すため、特に価値の高いセグメントです。
チャネル横断のプロモーション機会を見つけるには、EXCEPTを使います。たとえば、SELECT * FROM online_purchases EXCEPT SELECT * FROM in_store_transactionsは、オンラインのみで購入し、店舗では購入していない顧客を見つけます。企業は、これらの顧客に対して実店舗への来店を促すプロモーションを実施できます。
セグメンテーションのその先へ
これらのセグメントが特定できたら、さらに属性や購入履歴などの追加要因に基づいて細分化できます。顧客理解が粒度高くなることで、より精緻にマーケティング施策を設計できます。
集合演算子とJOINの違い
集合演算子もSQLのJOINも、複数のクエリからデータを組み合わせますが、動作は異なります。核心となる問いは、結合したいのが「行」か「列」かです。
| Feature | Set Operators | JOINs |
|---|---|---|
| Combines | Rows (stacks queries vertically) | Columns (widens rows horizontally) |
| Requires | Same column count and compatible data types | A shared key column between tables |
| Use when | Merging similar datasets, finding overlaps or differences between result sets | Enriching a row with related data from another table |
| Duplicate handling | UNION removes duplicates; UNION ALL keeps them |
Depends on join type and the data |
| Types | UNION, UNION ALL, INTERSECT, EXCEPT/MINUS |
INNER, LEFT, RIGHT, FULL OUTER, CROSS |
集合演算子の制約と考慮事項
SQLで集合演算子を使用する際は、クエリの性能、結果の正確性、使い勝手に影響するいくつかの制約や要因を考慮することが重要です。
データ型の互換性とNULL値
結果集合の対応する列は、互換性のあるデータ型でなければなりません。クエリを結合する前に、対応する列のデータ型が互換であることを確認してください。型の不一致は見落としやすいエラーの原因になります。
集合演算子は、DBMSや特定の演算子によってNULL値の扱いが異なる場合があります。エラーを避けるには、NULL値がどのように扱われるかを理解しておく必要があります。
性能への影響と重複行
集合演算は、特に大規模または複雑なデータセットを扱う場合、クエリ性能に大きく影響し得ます。インデックス、クエリ最適化、ハードウェア資源といった要因が性能に影響します。最適化手法やチューニング戦略は、ボトルネックの軽減に不可欠です。
デフォルトでは、集合演算子は結果集合から重複行を削除します。しかし、場合によっては重複行を保持する必要があります。重複に関する集合演算子の挙動を理解し、必要に応じて適切な手法で重複を扱ってください。
結果の並び順とメモリ制約
集合演算子は、最終的な出力の並び順を保証しません。結合後の結果をソートするには、最後のSELECT文の後、クエリ全体の一番最後にORDER BY句を1つだけ追加します。
特に大規模なデータセットを扱う場合、集合演算は多くのメモリとリソースを消費することがあります。性能低下やシステム不安定を避けるため、メモリやリソースの制約を考慮してください。
複雑さ、保守性、DBMS間の互換性
複数の集合演算子、サブクエリ、結合を含む複雑なクエリは、理解・保守・デバッグが難しくなります。可読性と保守性を高めるには、クエリを簡潔にし、十分にドキュメント化し、モジュール化してください。
集合演算子は、データベース管理システム(DBMS)によって構文や挙動に差異がある場合があります。クロスプラットフォームで互換性のあるSQLを書くには、これらの違いを把握しておくことが重要です。
まとめ
集合演算子は、どの行が重なるかを事前に知らなくても、結果集合を結合・比較するという特定の課題を解決します。
対象はUNION、INTERSECT、EXCEPT。結果集合の結合・比較・差し引きを行う3つの演算子です。
さらに学ぶなら、Joining Data in SQLのコースをご覧ください。
SQL演算子に関するFAQ
UNION ALL と UNION の違いは何ですか?
UNION ALLは、重複があっても両方のクエリからすべての行を含めます。UNIONは重複行を取り除きます。
SQLにおけるUNIONはJOINとどう違いますか?
UNIONはクエリの結果を縦方向に結合し、一方のクエリの行にもう一方の行を追加します。JOINは関連する列に基づいて行を対応付け、横方向に列を広げる形でテーブルを結合します。
集合演算を使用する際のパフォーマンス上の注意点はありますか?
集合演算は、特に大規模データセットを扱う場合に計算コストが高くなることがあります。個々のクエリを最適化し、可能な限りインデックスを使用して性能を向上させることが重要です。
EXCEPT と NOT IN の違いは何ですか?
EXCEPTとNOT INは類似の結果を返すことがありますが、NULL値の扱いが異なります。EXCEPTは行の比較においてNULL値を等しいものとして扱うため、同じ列位置にNULLが2つあると行は除外されます。一方、NOT INは、サブクエリにNULLが1つでも含まれると、SQLではNULL比較が未定義のため、結果として一切の行を返しません。大規模なデータセットでは、相関サブクエリのNOT INよりもEXCEPTの方が可読性が高い場合もあります。
集合演算子と一緒にORDER BYは使えますか?
はい、使えますが、クエリ全体の一番最後に1回だけです。集合演算の中で個々のSELECT文にORDER BYを入れることはできません。結合後の結果を並べ替えるには、最後のSELECT文の後にORDER BY句を1つだけ追加してください。
例:
SELECT name FROM employees
UNION
SELECT name FROM contractors
ORDER BY name ASC;MySQLはINTERSECTとEXCEPTをサポートしていますか?
MySQLはバージョン8.0.31でINTERSECTとEXCEPTをサポートしました。古いバージョンでは、INTERSECTはINNER JOINまたはINを使ったサブクエリで、EXCEPTはLEFT JOIN ... WHERE IS NULLまたはNOT INのサブクエリで代替する必要があります。OracleではEXCEPTの代わりにMINUSを使用します。