頻出中 ⏱ 6分★★★☆☆

自己結合

組織ツリー・ペア検索・WITH RECURSIVE まで完全習得

同じテーブルを異なるエイリアスで 2 回以上参照して JOIN する手法。同一テーブル内の行同士を比較・関連付けるために使う。

テーブルは 1 つだが、SQL では 2 つのテーブルが並んでいるかのように書く。エイリアスが必須。
社員テーブル(上司 ID で自己参照)
社員ID氏名役職上司ID
E01田中部長NULL
E02鈴木課長E01
E03佐藤課長E01
E04山田一般E02
-- 社員名と上司名を同時に取得
SELECT
  e.社員ID,
  e.氏名  AS 社員名,
  m.氏名  AS 上司名
FROM   社員 e                               -- e = 社員側
LEFT JOIN 社員 m ON e.上司ID = m.社員ID; -- m = 上司側
クエリ結果
社員名役職上司名
田中部長NULL(最上位)
鈴木課長田中
佐藤課長田中
山田一般鈴木
-- 同じ顧客が同日に複数回注文した組み合わせ
SELECT
  a.受注ID AS 受注A,
  b.受注ID AS 受注B,
  a.受注日
FROM   受注 a
JOIN   受注 b
  ON  a.顧客ID = b.顧客ID
  AND a.受注日 = b.受注日
  AND a.受注ID < b.受注ID;  -- 重複ペアを排除
a.ID < b.ID とすることで(A,B)と(B,A)の重複を防ぐ定番テクニック。
-- 連続した月次売上の前月比(ウィンドウ関数がない環境の代替)
SELECT
  cur.月,
  cur.売上                    AS 当月売上,
  pre.売上                    AS 前月売上,
  cur.売上 - pre.売上         AS 差分
FROM   月次売上 cur
LEFT JOIN 月次売上 pre
  ON  pre.年月 = cur.年月 - 1;
WITH RECURSIVE 組織 AS (
  -- アンカー:起点
  SELECT 社員ID, 氏名, 上司ID, 1 AS 階層
  FROM   社員 WHERE 社員ID = 'E01'
  UNION ALL
  -- 再帰:前ステップの結果を使って下位を探す
  SELECT e.社員ID, e.氏名, e.上司ID, o.階層 + 1
  FROM   社員 e
  JOIN   組織 o ON e.上司ID = o.社員ID
)
SELECT * FROM 組織 ORDER BY 階層;
階層の深さが不定な場合は固定の自己結合では対応できない。WITH RECURSIVE が必要。SQL:1999 標準、試験でも出題実績あり。

📝 理解度チェック

自己結合で上司 ID が NULL の最上位者も結果に含めるには何を使うか?
自己結合でペアの重複((A,B) と (B,A))を排除するための条件はどれか?
WITH RECURSIVE が必要になるのはどれか?

読了ボタンを押すとトップページの進捗に反映されます