Learn SQL/Intermediate/Joins/SQL JOIN Duplicate Rows

SQL JOIN Duplicate Rows

A join can multiply a row from the 'one' side once for every match on the 'many' side — genuinely surprising the first time you see it, and a common source of subtly wrong aggregate totals.

What & Why

When one marketing.campaigns row matches several marketing.leads rows, the campaign columns repeat once per matching lead. This is expected one-to-many behavior, but a later COUNT(*) or sum can be inflated if the intended grain is campaigns rather than joined rows.

See How It Works

BUSINESS QUESTION

Marketing compares the number of joined campaign-lead rows with the number of unique campaigns represented.

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
marketing.campaigns
idname
1Spring Launch
2Retention Webinar
3Finance Retargeting
4Enterprise Search
campaign.id = lead.campaign_id
marketing.leads
idcampaign_idsource
3011google_ads
3021google_ads
3032email
3043linkedin
Result set · 1 rows
campaign_namelead_idsource
Spring Launch301google_ads

Lead 301 adds one valid joined row without changing the source tables.

EXAMPLE QUERY
SELECT
  COUNT(*) AS joined_rows,
  COUNT(DISTINCT c.id) AS unique_campaigns
FROM marketing.campaigns c
JOIN marketing.leads l ON l.campaign_id = c.id;
Now You Try

Practice this concept

Marketing wants to compare joined campaign-lead rows with the number of unique campaigns represented.

Available schema
marketing

Prefix tables with marketing.table_name.

joined_rowsunique_campaigns
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