Talentd
FleetCode
Make and follow sheets.Sign in

Delete Duplicate Emails solution and explanation

EasyDatabase6 min read67.1% acceptanceUpdated 9 Apr 2026
LeetCode 196: Delete Duplicate Emails [SQL]Frederik Müller · 5:12 · 22K viewsWatch on YouTube
9 more videos on this problem
  1. LeetCode 196: Delete Duplicate Emails [SQL]Frederik Müller · 5:12 · 22K views
  2. Delete Duplicate Emails | Leetcode 196 | Crack SQL Interviews in 50 Qs #mysql #leetcodeLearn With Chirag · 4:43 · 10K views
  3. Leetcode 196 - Delete Duplicate Emails | SQL CTE Interview QuestionBig Tech Mindset · 6:01 · 3K views
  4. Leetcode 196 - Delete Duplicate Emails - Python Solution | FAANG InterviewsEveryday Data Science · 5:44 · 2K views
  5. LeetCode 196 - Delete Duplicate Emails (Python and SQL) [Easy]Ryan & Matt Data Science · 4:39 · 888 views
  6. SQL 196 Delete Duplicate EmailsKenneth Truong · 1:25 · 598 views
  7. 196. Delete Duplicate Emails | LeetCode | SQL | Solution | Interview ProblemData Alchemist · 5:02 · 335 views
  8. Leetcode #196 | Delete Duplicate EmailsEfficient Algos · 3:24 · 275 views
  9. LeetCode 196 | Delete Duplicate Emails | Delete | SQLSleepy Cracker · 1:33 · 139 views
  10. Leetcode | Easy | 196. Delete Duplicate EmailsSoftware Interviews Prep · 4:23 · 93 views

The problem

Table: Person

+-------------+---------+
| Column Name | Type    |
+-------------+---------+
| id          | int     |
| email       | varchar |
+-------------+---------+
id is the primary key (column with unique values) for this table.
Each row of this table contains an email. The emails will not contain uppercase letters.

Write a solution to delete all duplicate emails, keeping only one unique email with the smallest id.

For SQL users, please note that you are supposed to write a DELETE statement and not a SELECT one.

For Pandas users, please note that you are supposed to modify Person in place.

After running your script, the answer shown is the Person table. The driver will first compile and run your piece of code and then show the Person table. The final order of the Person table does not matter.

The result format is in the following example.

Example 1:

Input: 
Person table:
+----+------------------+
| id | email            |
+----+------------------+
| 1  | john@example.com |
| 2  | bob@example.com  |
| 3  | john@example.com |
+----+------------------+
Output: 
+----+------------------+
| id | email            |
+----+------------------+
| 1  | john@example.com |
| 2  | bob@example.com  |
+----+------------------+
Explanation: john@example.com is repeated two times. We keep the row with the smallest Id = 1.

Approach overview

Problem Overview: The Delete Duplicate Emails problem gives you a Person table containing id and email. Multiple rows may share the same email. The task is to remove duplicate records while keeping only the row with the smallest id for each email address.

Approach 1: SQL Using EXISTS Clause (O(n^2) time, O(1) space)

This method deletes rows if another row with the same email and a smaller id already exists. The query checks duplicates using an EXISTS subquery. For every row, the database scans for a matching email with a lower identifier. If found, the current row is removed. The approach is concise and works well in relational databases that optimize correlated subqueries. This technique relies heavily on concepts from SQL and database query optimization.

Approach 2: Using SQL with Self-Join (O(n^2) time, O(1) space)

A self-join compares rows in the same table. The query joins Person p1 with Person p2 where the email values match but p1.id > p2.id. That condition identifies duplicates that should be removed because a smaller id already exists. The delete operation targets the larger id rows. Self-joins are widely used for record comparison tasks and are often easier to reason about than nested subqueries.

Approach 3: Using a Hash Table (Dictionary) (O(n) time, O(n) space)

If the data is processed in application code instead of directly in SQL, a hash table can track emails already seen. Iterate through each row, and store the first occurrence of every email in a dictionary keyed by email address. If another row with the same email appears, mark it for deletion. Hash lookups run in constant time, so the entire process completes in linear time. This technique demonstrates the power of hash tables for duplicate detection.

Approach 4: SQL-Based Duplicate Removal with Aggregation (O(n log n) time, O(n) space)

Another SQL pattern uses grouping or window functions to identify the smallest id per email. For example, you compute the minimum id for each email group and delete rows whose id is larger than that minimum. This approach is especially useful when the database engine optimizes grouping operations efficiently. It is common in production SQL cleanup tasks where deduplication happens in batches.

Recommended for interviews: The SQL EXISTS solution is the most common answer because it is compact and expresses the logic directly: delete rows where a smaller id with the same email already exists. Mentioning the self-join variant shows deeper SQL understanding. In application-layer problems, the hash table solution demonstrates algorithmic thinking and achieves O(n) time.

Approach 1: SQL Using EXISTS Clause

This approach involves using the SQL EXISTS clause to handle the duplicates. By defining a subquery that checks for existence of another row with the same email but a smaller id, we can delete the current row if such a row exists.

DELETE p1 FROM Person p1 WHERE EXISTS (SELECT 1 FROM Person p2 WHERE p1.email = p2.email AND p1.id > p2.id);

This SQL query deletes rows from the Person table where there exists another row within the same table that has the same email but a smaller id. Thus, it ensures that only one unique email with the smallest id is retained.

Complexity

Time Complexity: O(n^2) due to the nested subquery, where n is the number of rows.
Space Complexity: O(1) as we are performing operations in place.

Try this approach in the editor

Approach 2: Using SQL with Self-Join

This approach uses a self-join to create joins within the same table. We join the table on the email field itself and use the condition to keep the entry with the smallest id.

DELETE FROM Person WHERE id NOT IN (SELECT min_id FROM (SELECT MIN(id) as min_id FROM Person GROUP BY email) as Temp);

The strategy involves creating a subquery that selects the minimum id for each email group. We then delete any row from Person where its id is not in this set of minimum ids.

Complexity

Time Complexity: O(n) primarily, because the operation groups by email and selects min in linear time, although the exact operations may vary by SQL engine.
Space Complexity: O(n) due to the creation of the temporary table holding min ids.

Try this approach in the editor

Approach 3: Approach 1: Using a Hash Table (Dictionary)

This approach involves using a hash table (or dictionary) to store the smallest ID for each unique email. As you iterate over each email entry, check if the email already exists in the hash table. If it does, compare IDs and update the hash table with the smaller ID if necessary. After processing all entries, use the hash table data to filter out duplicates by retaining entries with only the smallest IDs.

def remove_duplicates(persons):
    email_to_id = {}
    for person_id, email in persons:
        if email not in email_to_id or person_id < email_to_id[email]:
            email_to_id[email] = person_id

    return [(id, email) for email, id in email_to_id.items()]

persons = [(1, "john@example.com"), (2, "bob@example.com"), (3, "john@example.com")]
persons = remove_duplicates(persons)
for person in persons:
    print(person)
import java.util.*;

class Person {
    int id;
    String email;

    Person(int id, String email) {
        this.id = id;
        this.email = email;
    }
}

public class Main {

    public static void removeDuplicates(List<Person> persons) {
        Map<String, Integer> emailToId = new HashMap<>();
        for(Person person : persons) {
            if (!emailToId.containsKey(person.email) || person.id < emailToId.get(person.email)) {
                emailToId.put(person.email, person.id);
            }
        }

        List<Person> uniquePersons = new ArrayList<>();
        for(Map.Entry<String, Integer> entry : emailToId.entrySet()) {
            uniquePersons.add(new Person(entry.getValue(), entry.getKey()));
        }

        persons.clear();
        persons.addAll(uniquePersons);
    }

    public static void main(String[] args) {
        List<Person> persons = new ArrayList<>();
        persons.add(new Person(1, "john@example.com"));
        persons.add(new Person(2, "bob@example.com"));
        persons.add(new Person(3, "john@example.com"));

        removeDuplicates(persons);

        for (Person person : persons) {
            System.out.println(person.id + " " + person.email);
        }
    }
}
#include <iostream>
#include <unordered_map>
#include <vector>
#include <string>

struct Person {
    int id;
    std::string email;
};

void removeDuplicates(std::vector<Person>& persons) {
    std::unordered_map<std::string, int> emailToId;
    for (const auto& person : persons) {
        if (emailToId.find(person.email) == emailToId.end() || person.id < emailToId[person.email]) {
            emailToId[person.email] = person.id;
        }
    }

    std::vector<Person> uniquePersons;
    for (const auto& [email, id] : emailToId) {
        uniquePersons.push_back({id, email});
    }
    persons = std::move(uniquePersons);
}

int main() {
    std::vector<Person> persons = {{1, "john@example.com"}, {2, "bob@example.com"}, {3, "john@example.com"}};
    removeDuplicates(persons);
    for (const auto& person : persons) {
        std::cout << "{" << person.id << ", " << person.email << "}" << std::endl;
    }
    return 0;
}
function removeDuplicates(persons) {
    const emailToId = {};
    persons.forEach(({ id, email }) => {
        if (!emailToId[email] || id < emailToId[email]) {
            emailToId[email] = id;
        }
    });

    return Object.keys(emailToId).map(email => ({ id: emailToId[email], email }));
}

const persons = [
    { id: 1, email: "john@example.com" },
    { id: 2, email: "bob@example.com" },
    { id: 3, email: "john@example.com" }
];

const uniquePersons = removeDuplicates(persons);
console.log(uniquePersons);
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <stdbool.h>

#define MAX_RECORDS 100
#define EMAIL_LENGTH 256

typedef struct {
    int id;
    char email[EMAIL_LENGTH];
} Person;

int comparePersons(const void *a, const void *b) {
    Person *personA = (Person *)a;
    Person *personB = (Person *)b;
    return (personA->id - personB->id);
}

void removeDuplicates(Person persons[], int *size) {
    int n = *size;
    qsort(persons, n, sizeof(Person), comparePersons);

    int uniqueCount = 0;
    for (int i = 0; i < n; i++) {
        if (i == 0 || strcmp(persons[i].email, persons[uniqueCount - 1].email) != 0) {
            persons[uniqueCount++] = persons[i];
        }
    }

    *size = uniqueCount;
}

int main() {
    Person persons[MAX_RECORDS] = {
        {1, "john@example.com"},
        {2, "bob@example.com"},
        {3, "john@example.com"}
    };
    int n = 3;

    removeDuplicates(persons, &n);

    for (int i = 0; i < n; i++) {
        printf("%d %s\n", persons[i].id, persons[i].email);
    }

    return 0;
}
using System;
using System.Collections.Generic;
using System.Linq;

public class Person {
    public int Id { get; set; }
    public string Email { get; set; }
}

public class Program {
    public static void RemoveDuplicates(List<Person> persons) {
        var emailToId = new Dictionary<string, int>();
        foreach (var person in persons) {
            if (!emailToId.ContainsKey(person.Email) || person.Id < emailToId[person.Email]) {
                emailToId[person.Email] = person.Id;
            }
        }

        var uniquePersons = emailToId.Select(e => new Person { Id = e.Value, Email = e.Key }).ToList();

        persons.Clear();
        persons.AddRange(uniquePersons);
    }

    public static void Main(string[] args) {
        List<Person> persons = new List<Person> {
            new Person { Id = 1, Email = "john@example.com" },
            new Person { Id = 2, Email = "bob@example.com" },
            new Person { Id = 3, Email = "john@example.com" }
        };

        RemoveDuplicates(persons);

        foreach (var person in persons) {
            Console.WriteLine($"{person.Id} {person.Email}");
        }
    }
}

This Python solution uses a dictionary to map emails to their smallest IDs. As you iterate over person tuples, keep the lowest ID for each email in the dictionary. Finally, reconstruct the list of persons using the dictionary with unique emails and their smallest IDs.

Complexity

Time Complexity: O(n) for the single pass through the data.
Space Complexity: O(n) for the dictionary storing the emails and IDs.

Try this approach in the editor

Approach 4: Approach 2: SQL-Based Duplicate Removal

This approach is specifically designed for SQL contexts. The strategy here is to delete entries from a database such that for each email, only the entry with the smallest ID is retained. This involves using SQL commands to isolate entries that have duplicate emails (but higher IDs than the minimum for each specific email) and then deleting them.

DELETE FROM Person
WHERE id NOT IN (
  SELECT MIN(id)
  FROM Person
  GROUP BY email
);

This SQL solution uses a DELETE statement with a sub-query to identify entries that are not the minimum ID for a given email. The sub-query groups by email and selects the minimum ID for each group, effectively isolating the desired entries to be retained.

Complexity

Time Complexity: Generally O(n log n) for the grouping operation.
Space Complexity: O(n) with respect to holding email and associated data in memory during sub-query execution.

Try this approach in the editor

Complexity comparison

ApproachComplexity
SQL Using EXISTS Clause

Time Complexity: O(n^2) due to the nested subquery, where n is the number of rows.
Space Complexity: O(1) as we are performing operations in place.

Using SQL with Self-Join

Time Complexity: O(n) primarily, because the operation groups by email and selects min in linear time, although the exact operations may vary by SQL engine.
Space Complexity: O(n) due to the creation of the temporary table holding min ids.

Approach 1: Using a Hash Table (Dictionary)

Time Complexity: O(n) for the single pass through the data.
Space Complexity: O(n) for the dictionary storing the emails and IDs.

Approach 2: SQL-Based Duplicate Removal

Time Complexity: Generally O(n log n) for the grouping operation.
Space Complexity: O(n) with respect to holding email and associated data in memory during sub-query execution.

Complexity analysis

ApproachTimeSpaceWhen to Use
SQL Using EXISTS ClauseO(n^2)O(1)Standard SQL solution for deleting duplicates while keeping smallest id
SQL Self-JoinO(n^2)O(1)Useful when comparing rows within the same table
Hash Table (Dictionary)O(n)O(n)When processing data outside the database in application code
SQL Aggregation / GroupingO(n log n)O(n)When using GROUP BY or window functions to identify unique rows

Frequently asked questions

Is Delete Duplicate Emails easy or hard?

Delete Duplicate Emails is classified as an Easy problem. The challenge mainly tests SQL fundamentals such as self-joins, correlated subqueries, and understanding how to keep one record while deleting duplicates.

Delete Duplicate Emails Python/Java solution

In Python or Java, store emails in a hash map or set while iterating through the records. The first occurrence of an email is kept, and later duplicates are removed or skipped. This method runs in O(n) time and uses O(n) extra memory.

How to solve Delete Duplicate Emails in O(n)?

Use a hash table keyed by email address. Iterate through each record once and store the first occurrence of every email. When a duplicate email appears, mark that row for deletion. Hash lookups take constant time, so the full scan completes in O(n).

What is the best approach for Delete Duplicate Emails?

The most common solution uses a SQL EXISTS clause. It deletes a row when another row with the same email and a smaller id already exists. This approach directly expresses the rule of keeping the smallest id and is widely accepted in SQL interviews and database tasks.

Is Delete Duplicate Emails asked at Google/Amazon/Meta?

Database cleanup and deduplication problems like this appear frequently in interviews at companies such as Amazon and Google, especially for backend or data-focused roles. Candidates are expected to demonstrate strong SQL skills including joins, subqueries, and grouping.

What data structure is used in Delete Duplicate Emails?

In SQL solutions the database engine handles comparisons using joins or subqueries. When solved programmatically, a hash table (dictionary) is used to track emails already seen and detect duplicates efficiently.

What is the time complexity of Delete Duplicate Emails?

Most SQL approaches such as EXISTS or self-join comparisons run in O(n^2) time in the worst case because each row may be compared with other rows. If the data is processed in application code using a hash table, the complexity improves to O(n) time with O(n) additional space.

Comments

Now write it yourselfClose this page and solve Delete Duplicate Emails in the editor, without looking.
Practise Delete Duplicate Emails