Learn SQL/Intermediate/Joins/SQL FULL OUTER JOIN

SQL FULL OUTER JOIN

Keeps everything from BOTH sides — the union of what LEFT JOIN and RIGHT JOIN would each produce.

What & Why

FULL OUTER JOIN keeps every row from both tables — matched rows combine normally, and any row from either side with no match gets NULL filled in for the missing side. It's genuinely the union of a LEFT JOIN's result and a RIGHT JOIN's result.

See How It Works

BUSINESS QUESTION

Marketing reconciles filtered campaign and lead subsets so unmatched rows from both sides remain visible.

idnamechannelspendstart_dateend_datestatustarget_segment
1Spring Launchgoogle_ads55000.002024-01-152024-03-31activesmb
2Retention Webinaremail45000.002024-02-102024-04-15activeenterprise
3Finance Retargetinglinkedin50000.002024-03-122024-05-31activeenterprise
4Enterprise Searchgoogle_ads60000.002024-04-012024-06-30activeenterprise
idcampaign_idemailcreated_atqualified_atconverted_atlead_scoresourcecountry
3011ana@example.com2024-01-21 09:10:00+002024-01-22 11:00:00+002024-02-02 10:00:00+0086google_adsUS
3021ben@example.com2024-01-24 12:40:00+00NULLNULL52google_adsCA
3032chloe@example.com2024-02-16 08:30:00+002024-02-18 14:20:00+00NULL74emailGB
3043dev@example.com2024-03-20 17:15:00+002024-03-21 09:00:00+002024-04-04 16:00:00+0091linkedinUS
Trace the relationship row by row1× speed
campaign_subset · marketing.campaigns
idname
1Spring Launch
2Retention Webinar
4Enterprise Search
campaign.id = lead.campaign_id
lead_subset · marketing.leads
idcampaign_idsource
3011google_ads
3043linkedin
Result set · 1 rows
campaign_idnamelead_campaign_id
1Spring Launch1

Spring Launch and Lead 301 match across the two filtered internal-table subsets.

EXAMPLE QUERY
WITH campaign_subset AS (
  SELECT id, name
  FROM marketing.campaigns
  WHERE id IN (1, 2, 4)
),
lead_subset AS (
  SELECT id, campaign_id
  FROM marketing.leads
  WHERE id IN (301, 304)
)
SELECT
  c.id AS campaign_id,
  c.name,
  l.campaign_id AS lead_campaign_id
FROM campaign_subset c
FULL OUTER JOIN lead_subset l ON l.campaign_id = c.id
ORDER BY COALESCE(c.id, l.campaign_id), l.id;
Now You Try

Practice this concept

Marketing reconciles filtered campaign and lead subsets with unmatched rows on both sides.

Available schema
marketing

Prefix tables with marketing.table_name.

campaign_idnamelead_campaign_id
marketing.campaigns
ColumnType
idinteger
nametext
channeltext
spendnumeric
start_datedate
end_datedate
statustext
target_segmenttext
legacy_idtext
marketing.leads
ColumnType
idinteger
campaign_idinteger
emailtext
created_attimestamp with time zone
qualified_attimestamp with time zone
converted_attimestamp with time zone
lead_scoreinteger
sourcetext
countrytext
archive_statustext
query.sql
Intermediate business practice

Sign up free to try it on a real business scenario