RecipesDeduplicate rows
Deduplicate rows
Keep exactly one row per key when a table has accidental duplicates — and see the ones you dropped first.
The problem
A table has rows that should be unique on some key but aren't — a double-submitted order, a botched import, a bad join upstream. You want one row per key.
Look before you delete
orders 106 and 107 are the same customer, date, and amount — a duplicate
submission. Find the dupes first:
SQL playground
Loading editor…
⌘/Ctrl + Enter
The pattern
Number rows within each key group, keep rn = 1:
SQL playground
Loading editor…
⌘/Ctrl + Enter
The ORDER BY inside the window decides which copy you keep — here the
lowest order_id. Pick deliberately (earliest created_at, non-null-est row,
etc.).
Actually deleting them
-- Postgres / SQLite: delete by the rows you want to keep
DELETE FROM orders
WHERE order_id NOT IN (
SELECT MIN(order_id)
FROM orders
GROUP BY customer_id, order_date, amount
);-- Engines with deletable CTEs (Postgres, SQL Server)
WITH ranked AS (
SELECT ctid, ROW_NUMBER() OVER (
PARTITION BY customer_id, order_date, amount ORDER BY order_id
) AS rn
FROM orders
)
DELETE FROM orders WHERE ctid IN (SELECT ctid FROM ranked WHERE rn > 1);Gotchas
- Define "duplicate" precisely. Rows that differ only in an
updated_ator a surrogate id aren't duplicates for every purpose. Partition by the columns that actually define identity. SELECT DISTINCTonly helps when the entire row is identical. It can't keep "one row per customer" while returning other columns.- Run the
SELECTversion first, eyeball the count, then convert toDELETE.
- Top N per group — same ROW_NUMBER trick, N = 1.
- ROW_NUMBER — the numbering function.
- GROUP BY — for the "find the dupes" count.