PostgreSQLで検索結果を結合する! INTERSECTとEXCEPTを使ってみた

こんにちは。最近SQLを使う機会が多い関口です。
 
前回は検索結果を結合して取得する、代表的なUNIONとUNION ALLについて紹介しました。
PostgreSQLで検索結果を結合する!UNIONを使ってみた。
 
今回は、同じ検索結果の結合でも、UNIONとは違った結合の仕方ができる構文を紹介していきます!

検証で使用した環境は以下の通りです。

PostgreSQL
12.3

INTERSECTは、複数の検索結果(SELECT)全てに含まれているデータを取得できる構文です。検索結果同士の項目数およびデータ型が一致している必要があります。また、INTERSECTでは、DISTINCTと同様に、結果から重複している行を削除して取得します。
 
今回使用するテーブルは下記の通り作成しました。

// テーブル作成SQL
create table public.table_a (
  user_id integer not null
  , name character varying(50)
  , target_date date
);
create table public.table_b (
  user_id integer not null
  , name character varying(50)
  , target_date date
);

// テーブル内データ
SELECT * FROM table_a;
 user_id | name | target_date
---------+------+-------------
       1 | 杉本 | 2022-04-30
       1 | 杉本 | 2022-05-01
       2 | 白石 | 2022-04-30
       2 | 白石 | 2022-05-01
       2 | 白石 | 2022-05-02
       3 | 尾形 | 2022-04-30
       4 | 月島 | 2022-05-01
       5 | 鶴見 | 2022-05-02
       5 | 鶴見 | 2022-05-02
(9 行)

SELECT * FROM table_b;
 user_id | name | target_date
---------+------+-------------
       1 | 杉本 | 2022-04-30
       1 | 杉本 | 2022-05-01
       2 | 白石 | 2022-04-30
       2 | 白石 | 2022-05-02
       3 | 尾形 | 2022-04-30
(5 行)

それでは、実際にINTERSECTを使ってみましょう!
// 実行SQL
SELECT
  user_id
  , name
FROM
  table_a
INTERSECT
SELECT
  user_id
  , name
FROM
  table_b;

// 実行結果
 user_id | name
---------+------
       1 | 杉本
       2 | 白石
       3 | 尾形
(3 行)

table_aとtable_bの両方に存在するデータが取得できました! 重複データも削除されていますね。複数の検索結果で、重複するデータを取得したい場合はINTERSECTを使いましょう。

INTERSECT ALLは、INTERSECTと同様に、複数の検索結果(SELECT)全てに含まれているデータを取得できる構文です。INTERSECT ALLでは、結果から重複している行もそのまま取得されます。重複チェックを行わない分、INTERSECTよりも処理速度が速くなります。
 
先ほど使用したSQLを、INTERSECT ALLに変えて実行してみます!

// 実行SQL
SELECT
  user_id
  , name
FROM
  table_a
INTERSECT ALL
SELECT
  user_id
  , name
FROM
  table_b;

// 実行結果
 user_id | name
---------+------
       1 | 杉本
       1 | 杉本
       2 | 白石
       2 | 白石
       3 | 尾形
(5 行)

重複チェックで削除されていたデータも取得できました! 重複を削除しなくてもいい場合は、INTERSECT ALLを使用しましょう。

EXCEPTは、複数の検索結果(SELECT)のうち、1つめの検索結果にだけ存在するデータを取得できる構文です。検索結果同士の項目数およびデータ型が一致している必要があります。また、EXCEPTでは、DISTINCTと同様に、結果から重複している行を削除して取得します。

// 実行SQL
SELECT
  user_id
  , name
FROM
  table_a
EXCEPT
SELECT
  user_id
  , name
FROM
  table_b;

// 実行結果
 user_id | name
---------+------
       4 | 月島
       5 | 鶴見
(2 行)

table_aにだけ存在するデータが取得され、重複データは削除されていますね。複数の検索結果で、1つの検索結果にだけ存在するデータを取得したい場合はEXCEPTを使いましょう。

EXCEPT ALLは、EXCEPTと同様に、複数の検索結果(SELECT)のうち、1つめの検索結果にだけ存在するデータを取得できる構文です。EXCEPT ALLでは、結果から重複している行もそのまま取得されます。重複チェックを行わない分、EXCEPTよりも処理速度が速くなります。
 
先ほど使用したSQLを、EXCEPT ALLに変えて実行してみます!

// 実行SQL
SELECT
  user_id
  , name
FROM
  table_a
EXCEPT ALL
SELECT
  user_id
  , name
FROM
  table_b;

// 実行結果
 user_id | name
---------+------
       2 | 白石
       4 | 月島
       5 | 鶴見
       5 | 鶴見
(4 行)

重複チェックで削除されていたデータも取得できました! EXCEPTでは取得されていなかった「白石」が取得されているのは、table_bよりtable_aに「白石」が1つ多く存在するためです。重複を削除しなくてもいい場合は、EXCEPT ALLを使用しましょう。

いかがでしたでしょうか。
 
UNIONもINTERSECTもEXCEPTも、基本的な使い方は同じです。用途にあわせて使いわけていきたい今日この頃。SQLは奥が深いですね~!
 
 
 
 
《関連記事》

Timestamp型を時間だけで検索するときにインデックスを有効にする方法
技術
2017.10.25(Wed)

関数を使った条件はインデックスが効かない! Timestamp型を時間だけで検索するときにインデックスを有効にする方法

#SQL
 

PostgreSQLで日時を扱うTIPS7選
技術
2022.5.25(Wed)

実務でよく使う! PostgreSQLで日時を扱うTIPS7選

#SQL
 

運用、監視で役立つSQL
技術
2020.7.28(Tue)

PostgreSQLの運用で押さえておきたい! 運用、監視で役立つSQL

#SQL
 

記事をシェア
MOST VIEWED ARTICLES