A nightly de-duplication comparing every column reports nothing removed, yet each customer still appears twice — why?
answer
- nothing matched on the columns compared
- widest comparison removes the least
- the differing column describes the journey
- identity columns are nominated, not inherent
- too narrow collapses real records silently
basics
~20 sNothing was identical on every column, so nothing was removed. The two rows for each customer differ on a column that has nothing to do with the customer — an ingestion timestamp, a batch identifier, a loader's sequence number — and comparing on all columns makes exactly those repeats invisible.
solid answer
~50 sA clean removal report is not evidence that there were no repeats; it is evidence that no two rows matched on the column set you compared. Comparing on **every** column is the widest possible definition of a repeat and therefore removes the least: one differing value anywhere, including in a column the business has never heard of, makes two otherwise identical rows distinct. Find the offending column by taking the rows for one affected customer and comparing them value by value — the column that differs is almost always something the loading process added. Then nominate the columns that actually identify a customer record, compare on those, and accept that you have moved into the harder case: the surviving rows now disagree somewhere, so you need a stated rule for which occurrence survives rather than an inherited one.
go deeper
Recall that a de-duplication only removes rows matching on the columns it was told to compare, so removing nothing means nothing matched on those columns — not that the table is free of repeats.
Explain why comparing on every column removes the least, and identify the usual culprits: ingestion timestamps, batch identifiers and loader sequence numbers, which describe how the row arrived rather than what it says.
Demonstrate both failure directions. Too wide leaves the surplus visible downstream; too narrow silently collapses genuinely distinct records, and you would verify by checking that the number of distinct identifying combinations is unchanged by the reduction.
The call worth making is whether provenance belongs in the same table as the facts at all, given that every transform downstream then has to remember to exclude it, and what that recurring tax costs against separating the two.
## What "nothing removed" actually proved A de-duplication removes rows that match on the columns it was told to compare. When it removes nothing, exactly one thing has been established: **no two rows in this table match on that column set**. It has established nothing at all about whether the table holds the same customer twice. Comparing on every column is the widest possible definition of a repeat, and width works against you here. Two rows count as the same row only if they agree everywhere, so a single differing value anywhere — in a column nobody downstream reads, in a column the loader added, in a column that exists for auditing — is enough to make them distinct. The wider the comparison, the fewer repeats it can see. "Compare on all columns" is the setting that removes the least, and it is also the one people reach for when they have not decided what a row is supposed to mean. ## Finding the column that is defeating it The diagnosis is short and does not require reading much data: 1. **Take one affected customer** and pull their two rows. 2. **Compare them column by column** and list the columns whose values differ. 3. **Classify each differing column**: is it something about the customer, or something about how the row got here? The answer is nearly always in the second group: - an **ingestion or load timestamp** stamped when the row was written, so every reload differs by milliseconds; - a **batch, run or file identifier** recording which delivery brought the row; - a **sequence or line number** the loader assigned on the way in; - a **processed-by** or **environment** marker added by the pipeline. None of these say anything about the customer. They are provenance about the row's journey, sitting in the same rectangle as the facts, and a comparison that treats them as part of the record's identity is asking whether two rows arrived the same way rather than whether they describe the same thing. ## The columns that define a repeat are a choice There is no inherent identity column set. You choose one, and the choice decides which defects you can see and which removals are safe. Both directions have a failure mode, and neither announces itself: | | Comparison too wide (all columns) | Comparison too narrow | |---|---|---| | What it does | Removes nothing that differs anywhere, including on provenance | Collapses rows that are genuinely different records | | How it reports | Cleanly — zero or few rows removed | Cleanly — a large, satisfying removal count | | The tell | The repeat is still visible downstream in totals and counts | Totals fall, and history or per-period rows vanish | | Who notices | A consumer counting customers | Often nobody, until a comparison against the source is run | | The fix | Nominate the identifying columns explicitly | Add back the columns that legitimately distinguish two rows | The too-narrow direction is the more dangerous of the two, because its damage is a silent loss rather than a surviving surplus. Comparing a table of daily balances on the account alone, for example, collapses a year of history into one row per account and reports it as a successful clean-up. ## Choosing the set deliberately Say what one row of this table is supposed to mean, in a sentence, and the column set follows from it: - "One row per customer" gives the customer identifier alone. - "One row per customer per day" gives the customer identifier plus the date, and two rows for one customer on different days are then not repeats at all. - "One row per customer per version of their details" gives the identifier plus the version, and the repeats you are hunting are two rows claiming the same version. Everything outside that set is a value, not identity — including the provenance columns, which should never appear in it. ## What changes once you narrow it Narrowing the comparison moves you from the easy case to the hard one. Previously nothing matched, so nothing had to be decided. Now the two rows for each customer do match on the identifying columns while disagreeing elsewhere — on the balance, on the address, on the provenance columns at minimum. Removing one is no longer information-preserving, so the reduction needs a stated rule for which occurrence survives, on a column that genuinely encodes precedence, rather than whatever arrangement the previous step left behind. It is also worth checking, before writing that rule, whether two rows per customer were legitimate all along. If the producer emits a row per customer per delivery by design, the table means "one row per customer per delivery", the identifying set should include the delivery, and there was never a repeat to remove — only a misunderstanding about what the table holds. Discovering that is a better outcome than a correct de-duplication, because it removes the step rather than fixing it. ## The measurement to take alongside Whatever you conclude, take the two numbers that settle it: the row count, and the number of distinct combinations of the columns you nominated as identifying. Their ratio is how many rows per subject the table actually holds, and it is the number to quote in the ticket — far more informative than "the de-duplication removed nothing", which is what started this.
- How would you confirm the suspected column in one step rather than by eye?Count the distinct values of each candidate column within the rows of a single affected customer. Any column with more than one distinct value there is a candidate for what is defeating the comparison, and provenance columns will stand out because they differ for essentially every affected subject while the business columns mostly agree.
- Someone proposes comparing on the customer identifier alone. What do you check before agreeing?Whether two rows for one customer are ever legitimate. If the table holds one row per customer per day, per version or per delivery, that set is too narrow and will silently collapse real history into a single row while reporting a large, reassuring removal count. Get the sentence that says what one row means, and derive the set from it.
- Why are provenance columns dangerous specifically, rather than just unhelpful?Because they differ on essentially every reload, so including them guarantees the comparison finds nothing, and because they sit in the same rectangle as the facts and look like ordinary columns. A comparison that includes them is answering whether two rows arrived the same way, which is not a question anyone asked.
- Once the comparison is narrowed and rows are removed, how do you know you narrowed it correctly?Compare the number of distinct identifying combinations before and after: it must be unchanged, because a correct reduction removes surplus rows within subjects and never removes a subject. If that count falls, the set was too narrow and you have collapsed distinct records into one.
saying these in an interview costs you the question
- Reads a zero-removal report as proof that the table holds no repeats.
- Treats comparing on all columns as the safe or neutral default.
- Includes load timestamps or batch identifiers among the columns that define a repeat.
- Narrows to a single identifier without checking whether two rows per subject are legitimate.
- Assumes a large removal count means the fix worked.