Find duplicate values in one column
SELECT email, COUNT(*) AS total
FROM users
GROUP BY email
HAVING COUNT(*) > 1;
HAVING filters groups after they are counted, which is why it works here where WHERE cannot.
Find duplicates across multiple columns
SELECT first_name, last_name, COUNT(*) AS total
FROM users
GROUP BY first_name, last_name
HAVING COUNT(*) > 1;
List the actual duplicate rows
SELECT * FROM users
WHERE email IN (
SELECT email FROM users
GROUP BY email
HAVING COUNT(*) > 1
);
Delete duplicates and keep one
DELETE u1 FROM users u1
JOIN users u2
ON u1.email = u2.email
AND u1.id > u2.id;
This keeps the row with the lowest id and removes the rest. Always back up your table before running a delete like this.
Frequently asked questions
Why use HAVING instead of WHERE?
WHERE filters individual rows before grouping, so it cannot see a count. HAVING runs after the rows are grouped and counted, which is exactly when you know how many duplicates exist.
How do I count how many duplicates there are?
The COUNT(*) AS total column in the first query tells you how many times each value appears.
Want to get comfortable with GROUP BY and joins? Our free SQL course has a live, in-browser query runner.