How do I find duplicate rows in a table?
Group by the columns that should be unique, and ask for the groups that have more than one row. The key is to know which columns define the duplicate — a duplicate row is only a duplicate with respect to some key.
CREATE TABLE accounts (
id INTEGER PRIMARY KEY,
email VARCHAR(200) NOT NULL,
full_name VARCHAR(120) NOT NULL
);
INSERT INTO accounts (id, email, full_name) VALUES
(1, '[email protected]', 'Ada Lovelace'),
(2, '[email protected]', 'Ada Lovelace'),
(3, '[email protected]', 'Grace Hopper'),
(4, '[email protected]', 'Ana Lopez');
SELECT email,
COUNT(*) AS row_count,
GROUP_CONCAT(id) AS duplicate_ids
FROM accounts
GROUP BY email
HAVING COUNT(*) > 1;One row comes back: [email protected] with a count of 2 and the ids 1 and 2.
The shape of the query is the whole thing. GROUP BY email collapses the rows into groups by address, COUNT(*) counts each group, and HAVING keeps only the groups with more than one row. That is different from filtering in WHERE, which would remove rows before grouping and leave nothing to count.
What you get back is a summary, not the offending rows themselves. To see the full rows, you have to go back for them — either a self-join on the duplicated key, or a window function that counts each row's group and then filters on that count. The window version is usually the clearer one, because it produces the full row and lets you see which fields differ between the duplicates.
Three things to be careful about. If the duplicate key is nullable, NULLs group together and will show up as a duplicate group even though each NULL row may be legitimate. If you are looking for rows that are identical in every column, use GROUP BY across all the columns rather than just the business key. And the fastest way to check whether a table should have duplicates at all is to look for a UNIQUE constraint that is missing — if the database enforces the rule, the query is only ever a diagnostic.