How to delete duplicates on a MySQL table?
System Design practice on Codemia
Work through 120+ system design problems with detailed solutions, from rate limiters to multi-region storage.
Introduction
Deleting duplicates in MySQL starts with one important question: what counts as a duplicate. In most tables, you do not mean rows that are identical in every column. You mean rows that match on some business key, such as email address, while one row should be kept and the others removed.
The safest workflow is to define the duplicate rule, preview the rows that would be removed, and only then run the delete. Duplicate cleanup is one of those tasks where being careful is more important than being clever.
Identify the Duplicate Rule
Suppose this table stores users and you consider rows with the same email to be duplicates:
Before deleting anything, inspect the duplicates:
This tells you which keys appear more than once. It does not delete anything yet, which is exactly what you want at the start.
Delete Duplicates with a Self-Join
A common MySQL pattern is to keep the row with the smallest id and delete the rest:
How it works:
- '
u1is the row that may be deleted' - '
u2is another row with the same duplicate key' - '
u1.id > u2.idmeansu1is not the smallest id in its duplicate group'
So every duplicate except the kept row is removed.
This is a good general solution because it is easy to reason about and widely supported in MySQL.
Preview Before You Delete
Before running the DELETE, use the same join as a SELECT:
That preview step lets you verify the rows that will be removed. It is especially important when the duplicate key involves multiple columns.
For example, if duplicates are defined by both name and email, change the join condition accordingly.
MySQL 8 Window-Function Alternative
If you are using MySQL 8, you can also rank rows with ROW_NUMBER() and keep the first row in each duplicate group. That can be easier to read for complex duplicate rules, though the delete statement often still ends up using a subquery or join to remove the ranked rows.
The self-join approach remains a strong default because it is straightforward and well understood.
Prevent Duplicates from Coming Back
Cleaning the table once is only half the job. If duplicates are not allowed, add a constraint that prevents them from reappearing.
Example:
Without a uniqueness rule, the same data problem can return the next day.
Common Pitfalls
The biggest mistake is deleting duplicates before agreeing on which row should survive. Lowest id is common, but it is only one possible business rule.
Another mistake is running the delete without previewing the affected rows first.
A third issue is forgetting that duplicates may be defined by multiple columns, not just one.
Finally, do not clean the data and stop there. If duplicates are invalid, enforce that rule with a unique index or constraint.
Summary
- Define exactly what counts as a duplicate before deleting anything.
- Preview duplicate groups with
GROUP BY ... HAVING COUNT(*) > 1. - A self-join delete is a practical MySQL pattern for keeping one row and removing the rest.
- Use a preview
SELECTbefore the finalDELETE. - Add a unique constraint afterward if duplicates should never return.
Related reading
- How to delete from multiple tables in MySQL?
- How to delete multiple rows in DynamoDB?
- How to delete N numbers of documents in mongodb
- How to deploy changes to a Cassandra CQL schema
- How to design a distributed application using a Message Broker and a Database?
- How to design a distributed write-heavy data store
- How to design key schema to have only one DynamoDB table per application?
- How to design key schema to have only one DynamoDB table per application?

System Design Fundamentals
Build a strong foundation in designing scalable, reliable distributed systems.
View the courseTrack what you have practised
A free account saves your progress, solutions and study plan across every problem on Codemia.
System Design practice on Codemia
Work through 120+ system design problems with detailed solutions, from rate limiters to multi-region storage.