Joins I — semantics
Concept  ·  SQL & Querying

FULL OUTER JOIN keeps both sides' unmatched rows — with COALESCE keys and a side-status column it is the reconciliation report.

Reconciliation questions — 'do these two systems agree?' — are FULL OUTER questions: inner join shows only agreement, LEFT shows one direction of drift, FULL shows the whole picture in one pass. Three moves, one subtlety each: the key must COALESCE (the unmatched side's key is NULL); the status CASE must test the NULL sides before comparing values; and value comparison across nullable columns wants IS DISTINCT FROM.

See it break
shelf
itemn
X1
Y2
loans
itemn
Y2
Z3
The trap
SELECT
  s.item, 'ok'
  AS status
FROM
  shelf s
  JOIN loans l ON l.item = s.item
ORDER BY
  s.item;
The fix
SELECT
  COALESCE(s.item, l.item) AS item,
  CASE
    WHEN s.item IS NULL THEN 'b_only'
    WHEN l.item IS NULL THEN 'a_only'
    WHEN s.n IS DISTINCT FROM l.n THEN 'diff'
    ELSE 'ok'
  END AS status
FROM
  shelf s
  FULL OUTER JOIN loans l ON l.item = s.item
ORDER BY
  item;
The trap
Yok
The fix
Xa_only
Yok
Zb_only

The reconciliation missed the shelf-only and loan-only items — an INNER JOIN shows only agreement, where a FULL OUTER JOIN with a COALESCE key and a side-status CASE reports only-in-A, only-in-B, and matched.

The rule

The three-move construction (FULL OUTER, COALESCE key, status CASE); aggregate statuses for the summary line.

Shows up elsewhere
IS DISTINCT FROM (null-safe comparison)Anti-join three ways
Try one →Prove it in practice →