Deduplicate records with ROW_NUMBER() in a Query Activity
Keep only the most recent record for each email in a Data Extension using ROW_NUMBER() and PARTITION BY in a Marketing Cloud Query Activity.
Lead imports often bring the same email several times. If the target Data Extension uses email as its primary key, the Query Activity fails with a primary key violation. If it does not, you send the same email twice to the same person. The fix is to keep one record per email, usually the most recent one.
The snippet
SELECT
t.Email,
t.Name,
t.Source,
t.SignupDate
FROM (
SELECT
l.Email,
l.Name,
l.Source,
l.SignupDate,
ROW_NUMBER() OVER (
PARTITION BY LOWER(LTRIM(RTRIM(l.Email)))
ORDER BY l.SignupDate DESC
) AS rn
FROM Imported_Leads l
WHERE l.Email IS NOT NULL
AND l.Email <> ''
) t
WHERE t.rn = 1How it works
ROW_NUMBER()numbers the rows within each group, starting at 1.PARTITION BYdefines the group: here, each email.LOWER(LTRIM(RTRIM(...)))makes "Ana@X.com " and "ana@x.com" fall into the same group.ORDER BY SignupDate DESCmakes the most recent record number 1.- The outer
WHERE t.rn = 1keeps only that record.
The numbering has to live in a subquery: the WHERE of the same query cannot see the rn column.
Choosing the "best" record
The ORDER BY decides who wins a tie. A few common rules:
| I want to keep… | ORDER BY |
|---|---|
| the most recent one | SignupDate DESC |
| the oldest one (first source) | SignupDate ASC |
| the one with a phone number | CASE WHEN Phone IS NULL OR Phone = '' THEN 1 ELSE 0 END, SignupDate DESC |
In the Query Activity
- A target Data Extension with
Emailas the primary key. - The Overwrite action for a clean audience on every run, or Update to accumulate over time (the newest version of each email replaces the previous one).
- If the source Data Extension is large, filter by date inside the subquery so you stay within the Query Activity's 30-minute limit.
Related
| Title | Type | Platform | Belt | Reading |
|---|---|---|---|---|
| Data Views in SQL: who opened or clicked in the last 30 days | Tutorial | Marketing Cloud Engagement | Green | 3 min |
| AttributeValue and empty fields: AMPscript personalization that never breaks a send | Tutorial | Marketing Cloud Engagement | White | 3 min |
| How to use LookupRows in AMPscript (and when to switch to Lookup or LookupOrderedRows) | Tutorial | Marketing Cloud Engagement | Yellow | 3 min |
| Marketing Cloud REST API: OAuth 2.0 authentication and writing to a Data Extension | Tutorial | Marketing Cloud Engagement | Brown | 3 min |