How to Remove Duplicate Rows From a CSV File: 6 Methods Ranked by File Size (2026)
Remove duplicate rows from any CSV file: Excel, Google Sheets, Python pandas, command line, and SQL. Pick the right method by file size and keep the row you want.
Most guides send you straight to Excel's Remove Duplicates button. That button works fine on a 500 row list and quietly damages a 500,000 row export, mangling leading zeros, rewriting dates, and chopping off rows past Excel's limit without telling you. Before you pick a tool, answer two questions: how big is the file, and what actually counts as a duplicate.
First decide what "duplicate" actually means
Every method below does the same basic thing: it compares rows and keeps one copy. What changes is how you define "same," and getting that definition wrong is the single most common way people delete the wrong data.
Whole-row duplicates vs. duplicates on one key column
A whole-row duplicate means every single field matches, name, date, amount, everything. That's rare in real exports. Far more common is a duplicate on one key column, like the same order ID or email address appearing twice with slightly different timestamps or totals. If you dedupe on the whole row when you really needed to dedupe on just the order ID, you'll keep every near-miss row and the duplicates will survive.
Exact match vs. case/whitespace/trailing-space differences
"[email protected]" and "[email protected] " (with a trailing space) are the same email address to a human and two different strings to most deduplication tools. Decide upfront whether you need case-insensitive, whitespace-trimmed matching, because the default in Excel, Google Sheets, and SQL is a strict character-for-character comparison.
Which copy do you keep: first, last, or most complete?
When two rows represent the same record, one of them is usually better, a more recent timestamp, a filled-in phone number, a corrected spelling. Decide which copy wins before you run anything. "First" and "last" are easy to automate; "most complete" usually needs a manual pass or a custom rule (keep the row with fewer blank fields).
Method 1: Excel (for files under about 100,000 rows)
Excel is fine for small, one-off cleanups. It becomes risky the moment your file approaches its row ceiling or contains data types Excel likes to "fix" for you.
Using Data > Remove Duplicates and choosing the right columns
Select your data, open the Data tab, and choose Remove Duplicates. Excel lets you check or uncheck individual columns, which is exactly the whole-row-vs-key-column decision from above: check only the columns that define a duplicate for your use case, not every column in the sheet.
The leading-zero, long-number, and date-format traps before you save back to CSV
Excel auto-formats cells the moment it opens a CSV, and that formatting can change the underlying value when you save back out.
| What's in the CSV | What Excel shows after opening | What gets saved back to CSV |
|---|---|---|
| ZIP code "00501" | 501 | 501 (leading zeros gone) |
| Account number "1234567890123" | 1.23457E+12 | Scientific notation, precision lost |
| Date "2026-03-01" | 3/1/2026 (locale-dependent) | Reformatted date string |
If your file has any of these, format the affected columns as Text before you open the CSV, or avoid Excel for that file entirely. Microsoft's own guidance on importing and exporting CSV files covers these conversions in more depth.
Why Excel's row cap and silent truncation make it risky for big exports
Excel has a hard row limit per sheet. A CSV export larger than that limit gets truncated on open, with no dialog telling you rows were dropped, so you could "successfully" dedupe a file that was already missing a chunk of its data before you started. Treat Excel as a small-file tool, not a safety net for anything you haven't already verified is under that limit.
Method 2: Google Sheets with UNIQUE() (shareable, no install)
Google Sheets avoids the row cap problem of Excel for midsize files and keeps a formula-based audit trail instead of a one-click destructive action.
The =UNIQUE() formula and when it beats Remove Duplicates
The UNIQUE function returns a new range of only the distinct rows from a source range, leaving your original data untouched. That matters: if the formula result looks wrong, your source data is still there to fix and retry, unlike an in-place Remove Duplicates click.
Deduplicating on specific columns with QUERY or UNIQUE ranges
To dedupe on one key column instead of the whole row, wrap UNIQUE around just that column, or pair it with QUERY to pull back the full row for each distinct key. This is the Sheets equivalent of Excel's column checkboxes, just formula-driven and easier to audit.
Re-exporting cleanly to .csv without re-introducing dupes
Once UNIQUE() has produced your clean range, copy it, paste as values into a new sheet, and export that sheet as CSV. Exporting the tab that still contains the original formula and source data will just re-export the duplicates alongside the UNIQUE() output.
Method 3: Python pandas drop_duplicates() (the workhorse)
For anything repeatable, or any file where you don't trust a spreadsheet to preserve your data types, pandas is the most reliable general-purpose tool.
A copy-paste script: read_csv, drop_duplicates, to_csv
import pandas as pd
df = pd.read_csv("input.csv", dtype=str)
df = df.drop_duplicates()
df.to_csv("output.csv", index=False)
Reading every column as a string (dtype=str) with read_csv is what stops pandas from doing the same leading-zero and scientific-notation damage Excel does, since nothing gets silently reinterpreted as a number.
subset= to dedupe on key columns and keep='first'/'last'
df = df.drop_duplicates(subset=["order_id"], keep="last")
subset targets specific key columns instead of the whole row, and keep controls which copy survives, "first", "last", or False to drop every duplicate including the original.
Handling whitespace and case before you drop (str.strip, str.lower)
df["email"] = df["email"].str.strip().str.lower()
df = df.drop_duplicates(subset=["email"])
Normalize with str.strip() and str.lower() before deduping on a text column, otherwise " [email protected]" and "[email protected]" will both survive as separate rows.
Method 4: Command line with sort and awk (for files too big to open)
When a file is too large for Excel, Sheets, or even pandas to load comfortably into memory, standard Unix tools can stream through it line by line.
sort -u and when it's safe (and when it reorders your rows)
sort -u data.csv > deduped.csv
sort -u is fast and memory-efficient, but it alphabetizes your rows as a side effect, which also risks moving your header row out of position. It's a good fit when row order genuinely doesn't matter and you re-add the header afterward.
awk '!seen[$0]++' to dedupe while preserving original order
awk '!seen[$0]++' data.csv > deduped.csv
This one-liner, documented in the broader GNU awk manual, keeps the first occurrence of each line and preserves the original row order, which makes it the better default over sort -u for most CSV cleanup jobs.
Method 5: csvkit for header-aware CSV cleanup
Plain sort and awk treat every line as an undifferentiated string, including the header row. csvkit is a set of command line tools built specifically for CSV, so it understands columns and headers instead of raw text lines.
Installing csvkit and why header-awareness matters
csvkit installs as a Python package (pip install csvkit) and gives you commands like csvsort and csvformat that operate on named columns, which means you can sort or dedupe by a specific field without the header row getting treated as just another line of data.
csvsort and csvformat for normalizing before dedupe
Run csvformat first to normalize delimiters and encodings, then csvsort on your key column to group matching values together, which makes it far easier to spot or script around the surviving duplicates in a file too large to open in a spreadsheet.
Method 6: SQL (when the data already lives in a database)
If the CSV is really just an export of a database table, dedupe at the source instead of round-tripping through a spreadsheet or script.
SELECT DISTINCT vs. GROUP BY for row- and column-level dedupe
| Approach | Deduplicates on | Keeps which row | Best for |
|---|---|---|---|
| SELECT DISTINCT | Whole row | N/A, rows are identical | Exact whole-row duplicates |
| GROUP BY | Chosen key columns | One row per group (needs an aggregate) | Dedupe on a subset of columns |
| ROW_NUMBER() OVER (PARTITION BY ...) | Chosen key columns | Whichever row you rank first | Keeping the newest or "best" record |
SELECT DISTINCT only removes rows that are identical across every selected column. GROUP BY collapses rows that share the same key columns but needs an aggregate function (MIN, MAX, COUNT) to decide what happens to the other columns.
ROW_NUMBER() OVER (PARTITION BY ...) to keep the newest record
WITH ranked AS (
SELECT *,
ROW_NUMBER() OVER (PARTITION BY order_id ORDER BY updated_at DESC) AS rn
FROM orders_raw
)
SELECT * FROM ranked WHERE rn = 1;
This pattern, built on the window functions available in PostgreSQL and most modern databases, is the closest SQL equivalent to pandas' keep="last": it lets you explicitly choose which duplicate survives instead of relying on whatever order the database happens to store rows in.
Exporting the deduped result back out to CSV
Most database clients have a COPY or \copy command, or a GUI export button, that writes a query result straight to CSV. Export the final deduped query, not the raw table, so the duplicates never make it back into the file.
Pick your method in 30 seconds
If you only read one section, read this one and go fix your file.
The decision cheat sheet: file size x dedupe type to tool
| File size | Whole-row duplicates | Dedupe on one key column |
|---|---|---|
| Under ~100k rows | Excel Remove Duplicates | Excel, checking only the key column |
| Shareable, team-edited | Google Sheets UNIQUE() | Sheets UNIQUE() + QUERY |
| Any size, repeatable job | Python pandas drop_duplicates() | pandas with subset= |
| Too large to open anywhere | awk '!seen[$0]++' | csvkit (csvsort + custom script) |
| Already in a database | SQL SELECT DISTINCT | SQL ROW_NUMBER() OVER (PARTITION BY) |
| Method | Handles huge files | Preserves data types (zeros, long numbers) | Needs install |
|---|---|---|---|
| Excel | No | No | No |
| Google Sheets | No | Partial | No |
| Python pandas | Yes | Yes | Yes |
| Command line (sort/awk) | Yes | Yes | No (built in on Mac/Linux) |
| csvkit | Yes | Yes | Yes |
| SQL | Yes | Yes | Depends on existing database |
Clean your messy export now
If you're staring at a messy export right now, match your file size and dedupe type to the table above, run the matching command or formula from the method section, then verify the result before you trust it anywhere downstream.
Verify the result and avoid re-introducing duplicates
Deduplication isn't done the moment a tool finishes running. Confirm it actually did what you intended.
Count rows before and after so you know what was removed
Run a row count on the original file and the output file (wc -l on the command line, COUNTA in a spreadsheet, len(df) in pandas). If the difference is zero, your match criteria were too strict; if it's suspiciously large, you may have deduped on the wrong column.
Spot-check near-duplicates the tools missed (encoding, hidden characters)
Two rows can look identical and still fail an exact match because of invisible differences: a non-breaking space instead of a regular space, mismatched line endings, or different text encodings. Open a handful of suspicious rows in a plain text editor rather than a spreadsheet, which tends to hide these characters.
Trailing spaces, case, and the wrong key column deleting valid records
This is where most bad dedupes come from: trimming and lowercasing too aggressively and merging two genuinely different records (two different people with similar emails), or deduping on a key column that isn't actually unique to a single real-world entity (like deduping customers by name alone). Re-check your key column choice against a few rows you know should stay separate.
Fix the upstream source so the dupes don't come back
If the same CSV export keeps arriving with duplicates, the fix belongs upstream: a missing unique constraint in the source database, a form that allows resubmission, or an export job that runs twice. Cleaning the CSV every time is a workaround, not a fix.
Frequently asked questions
What's the difference between removing fully duplicate rows and removing duplicates based on one column? A fully duplicate row matches on every column; deduping on one column (like an email or order ID) treats two rows as duplicates even if other fields differ, and you have to decide which of those differing rows to keep.
How do I remove duplicate rows from a CSV that's too large to open in Excel?
Use a command line tool like awk '!seen[$0]++' for order-preserving dedupe, sort -u when order doesn't matter, or load it with Python pandas, none of which need to hold the whole file in a spreadsheet grid.
Will removing duplicates in Excel change my dates, leading zeros, or long ID numbers? It can. Excel reformats numbers and dates on import and export, so account numbers can turn into scientific notation and ZIP codes can lose leading zeros unless you format those columns as text first.
How can I keep the most recent (or most complete) row when removing duplicates?
In pandas, sort by your date column first, then use drop_duplicates(keep="last"). In SQL, use ROW_NUMBER() OVER (PARTITION BY key ORDER BY date DESC) and keep only rn = 1.
How do I remove duplicates that only differ by capitalization or extra spaces?
Normalize first: str.strip() and str.lower() in pandas, TRIM() and LOWER() in SQL, or a helper column in a spreadsheet, then dedupe on the normalized version.
Does sort -u or SELECT DISTINCT change the order of my rows?
sort -u always reorders rows alphabetically. SELECT DISTINCT doesn't guarantee any particular order unless you add an explicit ORDER BY. If order matters, use awk '!seen[$0]++' or add an explicit sort afterward.
How do I check how many duplicate rows were actually removed?
Compare row counts before and after with wc -l, COUNTA, or len(df). If the number removed doesn't match your expectation, re-check whether you deduped on the right column with the right matching rules.
There's no single best way to remove duplicate rows from a CSV, there's only the method that matches your file size and your actual definition of a duplicate. Get those two answers right first, and whichever tool from Excel to SQL you reach for next will do the job correctly the first time.
More from the blog
How to Do Keyword Research for a SaaS Blog: A 7-Step System That Starts With Your Product (2026)
A product-first keyword research process for SaaS blogs: find buyer-intent terms, score by business value, map to funnel stage, and build clusters that convert.
Freemium vs Free Trial for SaaS: How to Choose the Right Model (2026)
Freemium or free trial? A decision framework for SaaS founders based on time-to-value, CAC, and conversion math, plus when to run both and how to switch.