SQLのEXCEPTとEXCEPT ALLを使いこなす:違い・使い方・実務での活用例

こんにちは!このページでは、SQLの集合演算の一つ「EXCEPT」について、使用する際の注意点を整理しながら、その仕組みや使い方を紹介します。

試した環境

  • PostgreSQL 17.5

SQLの集合演算とは?

SQLの「集合演算」は、複数の SELECT 文の結果を組み合わせて、新たな結果セットを作成するための便利な機能です。
よく使われる演算子には、 UNIONINTERSECTEXCEPT(Oracleでは MINUS)などがあります。

これらの集合演算は直感的に理解しやすいため、「なんとなくのイメージで使っている」という方も少なくないかもしれません。
しかし、実際の動作がイメージと異なる場合もあり、意図しない結果につながることがあります。

そこで今回は、EXCEPT に焦点を当て、具体的なデータを使いながら、その仕組みや注意点を解説していきます。

EXCEPTとEXCEPT ALL

EXCEPTEXCEPT ALL は、複数の SELECT 文の結果から、最初の SELECT 文にのみ存在する行を抽出するための集合演算です。

両者の違いは、最初の SELECT 文にのみ存在する行の重複をどのように扱うかという点にあります。

EXCEPT は、重複を取り除いて一意な行のみを返します。一方、EXCEPT ALL は重複を含め、条件に合致するすべての行をそのまま返します。

なお、いずれも 使用する SELECT 文同士では、列の数および対応する列のデータ型が一致している必要があります。

EXCEPTのイメージ

「最初の SELECT 文 にのみ存在する行」という考え方は、「A − B = 結果」という式で表すことができます。
この関係を視覚的に捉えると、次のような図でイメージできます。

exceptAqueryBquery

このイメージ図をもとに処理の結果を想像すると、図の印象とより一致するのは EXCEPT ALL の挙動です。
一方、EXCEPT は結果がすべて一意な行(重複のない行)で構成されるため、図のイメージとはやや異なる結果になります。

EXCEPT の場合、単に A と B に共通する行(取り除かれる行)があるかどうかだけでなく、A の中に同じデータが複数含まれている場合でも、すべての重複が除かれる点に注意が必要です。

それでは、重複の扱いについて正しく理解するために、具体的なデータを用いて確認してみましょう。

ここでは「購入履歴」テーブルに登録されたデータを使って、実際にどのような結果が返されるのかを見ていきます。

購入履歴データを表示

 顧客名 | 購入年 
--------+--------
 加藤   | 2024
 加藤   | 2024
 高木   | 2024
 志村   | 2024
 仲本   | 2024
 仲本   | 2024
 高木   | 2025
 仲本   | 2025
 内村   | 2025
 仲本   | 2025
 高木   | 2023
 南原   | 2023
 杉本   | 2022

検証データの概要

「2024年の購入顧客(A)」と「2022年の購入顧客(B)」のデータを用います。

・2024年の購入データ(A)は 6件。
・2022年の購入データ(B)は 1件。
・両年にまたがって購入した顧客(共通する行)は存在しない。
・2024年のデータには、同じ顧客名が2行重複している。

まずは EXCEPT を使って実行

それでは、上記のデータを用いて、2024年の購入顧客(A)から2022年の顧客(B)を EXCEPT で除外し、実際の処理結果を確認してみましょう。

EXCEPTの結果を表示

SELECT customer_name FROM purchase_history where purchase_year = '2024'
EXCEPT
SELECT customer_name FROM purchase_history where purchase_year = '2022'
;
 customer_name 
---------------
 仲本
 高木
 志村
 加藤
(4 rows)

実行結果は4行でした。2024年と2022年のデータに共通する行は存在しなかったため、2024年のデータから差し引かれるものはありませんでした。
ただし、2024年のデータ内に重複する顧客名が2件含まれていたため、単純な差分(6行 − 0行 = 6行)ではなく、重複を除いた4行が結果として返されました。

このように EXCEPT は、最初の SELECT 文にのみ存在する行を抽出し、さらに重複行を除外して、一意な行のみを返す処理であることがわかります。

次に EXCEPT ALL を使って実行

続いて、同じクエリを EXCEPT ALL で実行します。

EXCEPT ALLの結果を表示

SELECT customer_name FROM purchase_history where purchase_year = '2024'
EXCEPT ALL
SELECT customer_name FROM purchase_history where purchase_year = '2022'
;
 customer_name 
---------------
 仲本
 仲本
 高木
 志村
 加藤
 加藤
(6 rows)

実行結果は、単純な差分である6行(6行 − 0行)がそのまま返されました。

このように、EXCEPT ALL は最初の SELECT 文にのみ存在する行をすべて抽出し、重複も含めて返す処理であることがわかります。

したがって、EXCEPTEXCEPT ALL では、結果の件数や内容に違いが生じる点に注意が必要です。

4つのSELECT文を組み合わせるケース

次は、4つの SELECT 文を組み合わせて EXCEPT を使うケースを見ていきましょう。例として、2025年に初めて購入した新規顧客名を抽出するシナリオを想定します。

このケースでは、2025年に初めて購入した顧客は1名で、購入回数も1回のみです。

EXCEPT を複数回使うと処理順が分かりにくくなる場合がありますが、基本的には最初の SELECT 文が基準となり、上から順に処理されます。この例では次のような順序で処理が行われます:
・まず、2025年のデータから2024年のデータを差し引き、
・その結果から2023年のデータをさらに引き、
・最後に、2022年のデータを差し引く

このようにして導き出されたデータ(重複なし)に対して、EXCEPTEXCEPT ALL の両方を使い、結果の違いを確認してみます。

EXCEPTの結果を表示

SELECT customer_name FROM purchase_history where purchase_year = '2025'
EXCEPT
SELECT customer_name FROM purchase_history where purchase_year = '2024'
EXCEPT
SELECT customer_name FROM purchase_history where purchase_year = '2023'
EXCEPT
SELECT customer_name FROM purchase_history where purchase_year = '2022'
;
 customer_name 
---------------
 内村
(1 row)
EXCEPT ALLの結果を表示

SELECT customer_name FROM purchase_history where purchase_year = '2025'
EXCEPT ALL
SELECT customer_name FROM purchase_history where purchase_year = '2024'
EXCEPT ALL
SELECT customer_name FROM purchase_history where purchase_year = '2023'
EXCEPT ALL
SELECT customer_name FROM purchase_history where purchase_year = '2022'
;
 customer_name 
---------------
 内村
(1 row)

実行結果は、データに重複がなかったため、EXCEPTEXCEPT ALL のどちらを使用しても、1行のみが返されました。

このように、対象データに重複が存在しない場合は、両者の実行結果に違いはありません。

実務での活用方法

では、実際の現場ではどのような場面で EXCEPTEXCEPT ALL が活用されているのでしょうか。

私が EXCEPTEXCEPT ALL をよく使うのは、 INTERSECT と組み合わせて、データの比較を行う場面です。特に、本番環境とテスト環境のデータ比較や、システム改修の前後でデータに差異がないかを検証する用途で活用しています。

まず INTERSECT を使って共通データを抽出し、その結果をもとに EXCEPT ALL を用いて差異のあるデータを特定する、という流れが多いです。

主に、主キーが設定されたテーブル同士を比較するケースが多いのですが、主キーのない SELECT 文を比較する場合には EXCEPT ALL の方が有効です。このようなケースでは、重複を含めた「レコード件数の差」も正確に把握したいことが多いためです。

注意点としては、EXCEPT ALL はすべてのRDBMSでサポートされているわけではないことです。対応していない環境で件数差を正確に扱いたい場合は、主キーの取り扱いを意識したクエリ設計が必要です。

SQLによる差分抽出のスマートさ

現場では、よくデータをExcelに貼り付けて目視や関数で比較する方法がとられがちですが、INTERSECTEXCEPT を用いたSQLベースの比較は、より効率的かつスマートに差分を抽出できます。これらの構文の特徴を理解しておくことで、データ検証の作業効率は大きく向上します。

業務コード内での使用頻度と実際の選択肢

ただし、実際のシステム開発において、EXCEPTEXCEPT ALL を業務コード内で使用する機会は、それほど多くはありません。理由としては、コード内でテーブル全体(すべてのカラム)を比較するケースが少なく、多くは主キーや一部のキー項目をもとに処理が行われるためです。

また、パフォーマンスや保守性の観点からは、より柔軟で最適化しやすい NOT EXISTS の方が選ばれる傾向にあります。実際、私自身も本番環境の業務コードで EXCEPT を使ったことはありません。

一時的な分析・検証には強力な選択肢

それでも、差分データの特定や一時的な検証作業など、開発や運用における補助的な用途では、INTERSECTEXCEPT の組み合わせは非常に有効で、強力な手段となります。

最終的なデータ確認のステップは、開発者だけでなく、運用・テストなどさまざまな立場で必要とされる工程です。これらの構文の挙動を理解しておくことで、トラブル対応やデータ検証をよりスムーズかつ確実に行えるようになるでしょう。

📌 今回のまとめ

今回は、SQLの「集合演算」の中から EXCEPTEXCEPT ALL を取り上げ、それぞれの違いについて解説しました。
特に EXCEPT における重複排除の仕組みを正しく理解し、用途に応じて使い分けることが大切です。

そのほかの集合演算子はこちらで紹介しています

👉 INTERSECT へ

👉 UNION へ