PostgreSQLにおけるJOIN演算の実践的解説

本解説では、PostgreSQL 14を用いて各種JOIN操作の動作原理と実際の挙動を検証します。MySQLとは異なり、PostgreSQLはFULL OUTER JOINをネイティブにサポートするため、包括的な比較が可能です。

JOINの分類と意味論

SQLにおける結合操作は、テーブル間の関係性に基づき、以下の6種類に大別されます(各外結合にはOUTERキーワードの省略形も存在し、合計8通りの記法が実用上登場します):

演算子 動作概要
INNER JOIN 両テーブルでON条件を満たすレコードのみを抽出
LEFT [OUTER] JOIN 左テーブルの全レコードを保持。右テーブルに該当がない場合はNULL埋め
RIGHT [OUTER] JOIN 右テーブルの全レコードを保持。左テーブルに該当がない場合はNULL埋め
FULL [OUTER] JOIN 両テーブルのいずれかで条件を満たすレコードをすべて含む(片方だけ存在する場合も含む)
SELF JOIN 同一テーブルを別名で2回参照し、自己参照的な関係を表現
CROSS JOIN デカルト積:左テーブル各行 × 右テーブル各行の全組み合わせを生成

サンプルデータの準備

検証用に以下の2つのテーブルを作成し、テストデータを挿入します:

CREATE TABLE participants (
  pid SERIAL PRIMARY KEY,
  name TEXT NOT NULL,
  region TEXT
);

CREATE TABLE registrations (
  rid SERIAL PRIMARY KEY,
  participant_id INT,
  event_code TEXT,
  status TEXT DEFAULT 'active'
);

INSERT INTO participants (pid, name, region) VALUES
  (1, 'Aragorn', 'Gondor'),
  (2, 'Legolas', 'Mirkwood'),
  (5, 'Gimli', 'Erebor'),
  (6, 'Frodo', 'Shire');

INSERT INTO registrations (participant_id, event_code, status) VALUES
  (1, 'ELF-2024', 'confirmed'),
  (2, 'DWARF-2024', 'pending'),
  (3, 'HOBBIT-2024', 'cancelled'),
  (4, 'WIZARD-2024', 'confirmed');

INNER JOIN:共通キーを持つレコードのみを取得

参加者と登録情報の両方に存在するIDのみを抽出します。

SELECT 
  p.pid AS participant_id,
  p.name,
  r.event_code,
  r.status
FROM participants p
INNER JOIN registrations r ON p.pid = r.participant_id;

このクエリは、pid = 1 および pid = 2 のみを返します。他のID(3, 4, 5, 6)は片方のテーブルにしか存在しないため除外されます。

OUTER JOINの応用パターン

LEFT JOIN:主テーブルの完全なカバレッジ

すべての参加者を対象に、登録状況を付与します。

SELECT 
  p.name,
  COALESCE(r.event_code, 'not registered') AS event,
  COALESCE(r.status, 'N/A') AS reg_status
FROM participants p
LEFT JOIN registrations r ON p.pid = r.participant_id;

LEFT JOIN + NULLフィルタ:未登録者を特定

登録情報がまったくない参加者を抽出するには、右テーブルの結合キーがNULLである条件を追加します。

SELECT p.name
FROM participants p
LEFT JOIN registrations r ON p.pid = r.participant_id
WHERE r.rid IS NULL;

FULL OUTER JOIN:双方の孤児レコードを可視化

参加者テーブルにも登録テーブルにも存在しない「不整合」を一括で確認できます。

SELECT 
  p.name AS participant_name,
  r.event_code,
  CASE 
    WHEN p.pid IS NULL THEN 'orphan_in_registrations'
    WHEN r.rid IS NULL THEN 'orphan_in_participants'
    ELSE 'matched'
  END AS record_status
FROM participants p
FULL OUTER JOIN registrations r ON p.pid = r.participant_id;

CROSS JOIN:組み合わせ全探索

あらゆる参加者とイベントコードのペアを生成するユースケース(例:初期登録フォームのテンプレート生成)に利用されます。

SELECT 
  p.name,
  e.code AS available_event
FROM participants p
CROSS JOIN (VALUES ('ELF-2024'), ('DWARF-2024'), ('HOBBIT-2024')) AS e(code);

このクエリは、4人の参加者 × 3つのイベントコード = 合計12行の結果を返します。

タグ: PostgreSQL SQL JOIN relational-database outer-join

6月10日 21:23 投稿