同じ構造の2つテーブルでデータに差が在る場合、異なるデータがあるレコードを抽出する方法について。

UNIONとEXCEPTION ALLを使った方法

SQLの基本形は以下のようになります。

(
  SELECT * FROM [テーブル1]
  EXCEPT ALL SELECT * FROM [テーブル2]
) UNION ALL (
  SELECT * FROM [テーブル2]
  EXCEPT ALL SELECT * FROM [テーブル1]
)

ただし、このままだと結果が分かりづらくなるので、テーブル名表示用カラムの追加と、プライマリキーとテーブル名でソートするようなORDER BYを追加します。 テーブル名用のカラム名が衝突する場合は別の名前にしておきます。

(
  SELECT '[テーブル1]' AS _table_name, * FROM [テーブル1]
  EXCEPT ALL SELECT * FROM [テーブル2]
) UNION ALL (
  SELECT '[テーブル2]' AS _table_name, * FROM [テーブル2]
  EXCEPT ALL SELECT * FROM [テーブル1]
) ORDER BY [プライマリーキー], _table_name

サンプル

テーブルusersとテーブルのusers_oldが以下のようになっている例を考えます。

testdb=# SELECT * FROM users ORDER BY id;
 id |    name    
----+------------
  1 | 山田たろう
  2 | 佐藤花子
  3 | 山下一郎
(3 行)

testdb=# SELECT * FROM users_old ORDER BY id;
 id |   name   
----+----------
  1 | 山田太郎
  2 | 佐藤花子
  3 | 山下次郎
(3 行)

上記の2つのテーブルについて差分を取る場合、基本形のSQLでは以下のようになります。

(
  SELECT * FROM users
  EXCEPT ALL SELECT * FROM users_old
) UNION ALL (
  SELECT * FROM users_old
  EXCEPT ALL SELECT * FROM users
) 

これを実行すると、結果は以下のようになります。データの順序を指定していないので、データが多いと非常に比較しづらくなります。

testdb=# (
testdb(#   SELECT * FROM users
testdb(#   EXCEPT ALL SELECT * FROM users_old
testdb(# ) UNION ALL (
testdb(#   SELECT * FROM users_old
testdb(#   EXCEPT ALL SELECT * FROM users
testdb(# );
 id |    name    
----+------------
  3 | 山下一郎
  1 | 山田たろう
  3 | 山下次郎
  1 | 山田太郎
(4 行)

比較しやすくするため、テーブル名表示用カラムの追加と、分かりやすくするためのソート用ORDER BYを追加します。このSQLは以下のようになります。

(
  SELECT 'users' AS _table_name, * FROM users
  EXCEPT ALL SELECT 'users' AS table_name, * FROM users_old
) UNION ALL (
  SELECT 'users_old' AS _table_name, * FROM users_old
  EXCEPT ALL SELECT 'users_old' AS table_name, * FROM users
) ORDER BY id, _table_name
実行すると以下のようになり、より見やすくなります。
testdb=# (
testdb(#   SELECT 'users' AS _table_name, * FROM users
testdb(#   EXCEPT ALL SELECT 'users' AS table_name, * FROM users_old
testdb(# ) UNION ALL (
testdb(#   SELECT 'users_old' AS _table_name, * FROM users_old
testdb(#   EXCEPT ALL SELECT 'users_old' AS table_name, * FROM users
testdb(# ) ORDER BY id, _table_name;
 _table_name | id |    name    
-------------+----+------------
 users       |  1 | 山田たろう
 users_old   |  1 | 山田太郎
 users       |  3 | 山下一郎
 users_old   |  3 | 山下次郎
(4 行)