Duplicates
The same business entity appears more than once when it should be unique.
A polished chart built on broken data is still broken. Validation is how analysts earn the right to make a recommendation.
Before explaining why the business changed, prove that the data did not change underneath you.
“Dhaka looks terrible today. Before we call Operations, can you confirm whether this is real?”
This is where a strong analyst resists storytelling. A dramatic metric can be caused by a real operational problem, a delayed pipeline, a changed mapping, duplicated rows or a broken filter.
Your first job is not diagnosis. It is trust classification: can this result be used yet?
The same business entity appears more than once when it should be unique.
Nulls or absent rows are concentrated in important segments or periods.
Keys fail to match or one-to-many relationships multiply measures.
Recent data is incomplete, late or stalled.
A field or business rule changed but the analysis did not.
Dates, amounts, statuses or rates violate business constraints.
Do not treat a surprising business result as interesting until you have ruled out boring data failures.
Uniqueness is behaving as expected.
This is why COUNT(*) and COUNT(DISTINCT order_id) belong in your first-pass QA.
| Check | Yesterday | Today | Change |
|---|---|---|---|
| orders row count | 148,220 | 154,870 | +4.5% |
| distinct order_id | 148,220 | 148,230 | +0.01% |
| gross_value sum | $3.84M | $4.09M | +6.5% |
| null city_id | 0.3% | 7.8% | +7.5 pp |
| payment rows / order | 1.12 | 1.48 | +32% |
| last event loaded | 09:58 | 08:14 | -104 min |
The row count rose while distinct orders barely moved. Null city IDs spiked. Payment rows per order increased. And the newest event is almost two hours older than yesterday's comparable load.
Those are strong reasons to investigate the pipeline before interpreting a city-level decline.
SELECT
COUNT(*) AS row_count,
COUNT(DISTINCT order_id) AS distinct_orders,
COUNT(*) - COUNT(DISTINCT order_id) AS duplicate_rows
FROM orders
WHERE created_at::date = CURRENT_DATE; SELECT
MAX(updated_at) AS latest_row_updated_at,
MAX(created_at) AS latest_business_event_at
FROM orders; WITH before_join AS (
SELECT
COUNT(DISTINCT order_id) AS orders,
SUM(gross_value) AS gmv
FROM orders
),
after_join AS (
SELECT
COUNT(DISTINCT o.order_id) AS orders,
SUM(o.gross_value) AS gmv
FROM orders o
LEFT JOIN payments p
ON o.order_id = p.order_id
)
SELECT * FROM before_join
UNION ALL
SELECT * FROM after_join; I have an analysis showing Dhaka completed orders down 34% day over day.
Before explaining the business cause, act as a data-quality reviewer.
Known observations:
- table row count +4.5%
- distinct order_id roughly flat
- null city_id increased from 0.3% to 7.8%
- payment rows per order increased from 1.12 to 1.48
- newest loaded business event is 104 minutes older than yesterday's comparable load
Your job:
1. Rank the most plausible data-quality risks.
2. Propose SQL checks for uniqueness, nulls, freshness and join inflation.
3. State what evidence would make the result decision-ready.
4. Do not explain the business decline until the integrity checks pass. Did row count and distinct business-key count move as expected?
Are supposed-to-be-unique keys actually unique?
Did null rates change by field, segment or date?
Did a join change the number of base entities?
Do sums and rates reconcile to a simpler source query?
Is the latest available timestamp fresh enough for this decision?
Are extreme or impossible values explainable?
Could a schema or business-rule change explain the anomaly?
A city dashboard says orders collapsed 34%. Inject duplicates, null mappings and stale data, then decide whether to escalate.
“Dhaka demand collapsed 34%, so Operations needs to investigate immediately.”
“The city-level decline is not yet trustworthy because city mapping and data freshness changed materially.”
Reprocessed city mapping, complete event freshness and reconciled order counts should reproduce the decline before escalation.
Separate data confidence from business confidence. A plausible number is not automatically a usable number.
Every important analysis should contain at least one control number you already trust, one freshness check and one explicit reconciliation step.