Data Cleaning and Quality Validation in SQL Questions
Handle NULL values, duplicates, and data type issues within queries. Implement data validation checks (row counts, value distributions, date ranges). Practice identifying and documenting data quality issues that impact analysis reliability.
HardTechnical
90 practiced
Define a set of KPIs for data quality in a product analytics platform (examples: timely-load-rate, row-reconciliation-failures, schema-drift-rate, critical-null-rate). For each KPI, state how you would calculate it in SQL, acceptable thresholds, and how to operationalize into dashboards, SLAs, and team responsibilities.
Sample Answer
Below are a prioritized set of KPIs for data quality in a product-analytics platform, each with a SQL calculation pattern, reasonable thresholds, and operationalization (dashboards, SLAs, team ownership).1) Timely Load Rate- Calculation (per dataset/day):- Threshold: >= 99% daily- Ops: Dashboard: daily time-series + heatmap by dataset; SLA: alert if < 99% for 1 day or < 95% for any dataset; Owner: ETL engineers on-call, escalation to Data Platform lead.2) Row Reconciliation Failure Rate- Calculation (compare source vs warehouse counts):- Threshold: <= 0.5% of tables; critical tables 0%- Ops: Dashboard: per-table diff, top offenders; SLA: failure triggers automated roll-forward job + incident within 2 hours; Owner: Data QA + owning product analytics team.3) Schema Drift Rate- Calculation (daily distinct schema-change events / tables):- Threshold: <= 1% daily; unexpected change = immediate alert- Ops: Dashboard: change log, diffs, preview impact; SLA: notify owners within 30 mins; Owner: Data Platform for detection, Producer teams for remediation.4) Critical Null Rate (for required fields)- Calculation:- Threshold: < 0.1% for critical keys; <1% for secondary- Ops: Dashboard: per-field trend + alert on spike; SLA: owner investigates within 4 hours; Owner: Source system/product team.5) Duplicate Row Rate (idempotency)- Calculation:- Threshold: < 0.01%- Ops: Dashboard: top duplicate causes; SLA: fix within 24 hours; Owner: Producer teams, dedupe in pipeline by ETL team.6) Freshness Lag (median / 95th percentile)- Calculation (minutes):- Threshold: median < 10 min, p95 < 60 min- Ops: Dashboard: per-stream SLA view; SLA: alert if p95 breaches; Owner: Streaming/ETL team.7) Value-Drift / Anomaly Rate (feature distribution divergence)- Calculation (daily columns with JS divergence > threshold):- Threshold: JS divergence > 0.2 flagged- Ops: Dashboard: drift score, cohort impact; SLA: Data Scientist review within 48 hours; Owner: Modeling team + Product owners.8) Completeness Rate (per event type/session)- Calculation:- Threshold: >= 99% for core events- Ops: Dashboard: funnel completeness; SLA: notify product owner if < 98%; Owner: Product + front-end engineering.Operationalization best practices- Dashboards: single “Data Quality Home” summarizing KPIs, drilldowns per dataset/table/column, SLA state (green/yellow/red). Provide historical trends, top-10 offenders, and links to recent incidents and runbooks.- Alerts & SLAs: tiered alerting (warning -> page -> incident) with runbooks and required response times per KPI. Automate remediation where safe (retries, backfills), otherwise create incidents.- Roles & RACI: Data Platform owns detection & tooling; ETL/Streaming owns pipelines; Source Product teams own data quality at source; Data Scientists/model owners own downstream model/data impact. Define escalation paths and weekly data-quality reviews.- Measurement & Review: weekly DQ review, monthly SLA scorecard, continuous improvement via root-cause tracking and preventive fixes.This set balances operational detection, business impact, and clear ownership so data scientists can trust inputs and focus on modeling and insight.
sql
SELECT 1.0 * SUM(CASE WHEN loaded_at <= expected_load_time THEN 1 ELSE 0 END) / COUNT(*) AS timely_rate
FROM dataset_loads
WHERE load_date = '2025-12-05';sql
SELECT 1.0 * SUM(CASE WHEN src_count != wh_count THEN 1 ELSE 0 END) / COUNT(*) AS reconciliation_fail_rate
FROM reconciliation_reports
WHERE report_date = '2025-12-05';sql
SELECT 1.0 * COUNT(DISTINCT table_name) FILTER (WHERE change_detected = TRUE) / COUNT(DISTINCT table_name) AS schema_drift_rate
FROM schema_events
WHERE event_date = '2025-12-05';sql
SELECT column_name,
1.0 * SUM(CASE WHEN col IS NULL THEN 1 ELSE 0 END)/COUNT(*) AS null_rate
FROM analytics_table
GROUP BY column_name
HAVING is_critical = TRUE;sql
SELECT 1.0 * SUM(cnt-1) / SUM(cnt) AS duplicate_rate
FROM (
SELECT event_id, COUNT(*) AS cnt
FROM events
GROUP BY event_id
) t;sql
SELECT
percentile_cont(0.5) WITHIN GROUP (ORDER BY EXTRACT(EPOCH FROM (now()-ingest_time))/60) AS median_min,
percentile_cont(0.95) WITHIN GROUP (ORDER BY EXTRACT(EPOCH FROM (now()-ingest_time))/60) AS p95_min
FROM table_ingest_times;sql
SELECT column_name, js_divergence
FROM feature_drift_metrics
WHERE event_date = '2025-12-05' AND js_divergence > 0.2;sql
SELECT event_type, 1.0 * COUNT(*) FILTER (WHERE required_fields_present) / COUNT(*) AS completeness
FROM events
GROUP BY event_type;HardTechnical
89 practiced
Your data-quality suite of SQL queries takes several hours and blocks nightly pipelines. Propose SQL and architectural strategies to optimize runtime: consider materialized views, incremental validation tables, sampling, approximate algorithms, partition pruning, and where to trade accuracy for speed. Provide example SQL snippets or pseudo-SQL for key optimizations.
Sample Answer
Approach: treat the suite as an ETL/validation pipeline problem — minimize recomputation, push work to precomputed structures, and accept bounded approximation where business impact is small. Mix strategies: materialized views / incremental validation tables for deterministic checks; sampling or approximate algorithms for high-cost aggregate checks; partition pruning and predicate pushdown to reduce scanned data; parallelize and budget checks by SLA.Key strategies + example SQLs1) Materialized views for expensive joins/aggregates (refresh incremental)Create MV that pre-aggregates stable columns daily, refresh CONCURRENTLY or ON COMMIT if supported.2) Incremental validation tables: validate only new/changed partitionsTrack last_validated_max_ts and validate delta.3) Partition pruning / predicate pushdownEnsure queries filter on partition key and use proper types/indexes.4) Sampling for expensive full-table checks (trade accuracy)Use systematic sampling with reproducible seed; raise full-check only on anomalies.5) Approximate algorithms (HyperLogLog, approx_count_distinct)Use DB functions for cardinality or quantiles.6) Multi-tier checks & alerting (where to trade accuracy)- Tier 1 (fast, approximate): sampling, HLL; run every hour. If anomalies -> Tier 2.- Tier 2 (targeted full): run deterministic checks only on suspect partitions/entities.- Tier 3 (full run): nightly full-run for critical tables only.7) Parallelization and resource controlsSplit work by partition range and run in parallel workers; use query hints or resource pools to avoid blocking pipelines.Metrics and safeguards- Define acceptable error bounds (e.g., ±2% on sampled aggregates).- Track false-negative risk and run full validations nightly for high-risk tables.- Measure runtime, scan bytes, and failures per run to prove improvements.Trade-offs: sampling/approximation reduces cost but increases false negatives; use thresholds to escalate. Materialized views add storage and maintenance cost but dramatically reduce compute.
sql
CREATE MATERIALIZED VIEW mv_user_events_daily AS
SELECT user_id, event_date, COUNT(*) AS events, SUM(value) AS total_value
FROM raw.events
GROUP BY user_id, event_date;
-- nightly: REFRESH MATERIALIZED VIEW CONCURRENTLY mv_user_events_daily;sql
INSERT INTO validation_failures (run_ts, id, reason)
SELECT now(), e.id, 'missing_fk'
FROM raw.events e
LEFT JOIN dim.users u ON e.user_id = u.id
WHERE e.event_ts > (SELECT last_validated_ts FROM validate_meta)
AND e.event_dt >= current_date - INTERVAL '7 days';
-- update meta afterwardssql
SELECT COUNT(*) FROM raw.events
WHERE event_date BETWEEN '2025-12-01' AND '2025-12-02' -- prunes partitions
AND event_type = 'purchase';sql
-- 1% sample
SELECT user_id, SUM(value) FROM raw.events TABLESAMPLE BERNOULLI (1)
WHERE event_date >= '2025-12-01'
GROUP BY user_id;sql
SELECT approx_count_distinct(user_id) FROM raw.events WHERE event_date = '2025-12-01';
-- or use t-digest/approx_percentile for latency-sensitive quantilesHardSystem Design
93 practiced
Design a validation architecture for streaming data that flows from Kafka to a data warehouse. Requirements: near-real-time deduplication, detection of schema drift, handling late-arriving events, and alerting. Describe components (stream processors, schema registry, validation service), what checks run in-stream vs in-batch, and trade-offs between latency and validation depth.
Sample Answer
Requirements:- Near-real-time deduplication- Schema-drift detection- Late-arriving events handling- Alerting- Push data into warehouse (e.g., Snowflake/BigQuery)High-level architecture:Producer → Kafka topics → Stream processor (Flink/ksqlDB/Spark Structured Streaming) → Validation service + Schema Registry → Dead-letter / Repair topic → Sink to Data Warehouse (micro-batches) → Batch validator / Reconciliation job → Monitoring & Alerting (Prometheus + Grafana, PagerDuty)Components & responsibilities:- Schema Registry (Confluent/Apicurio): store canonical schemas, versions, compatibility rules; enforce at producer or ingest.- Stream Processor: lightweight, low-latency checks; do near-real-time deduplication (stateful keyed dedupe using event id/window), basic schema validation (required fields, types), enrichment, route invalid or suspect records to repair topic.- Validation Service (microservice): richer rules that are heavier (cross-record consistency, business rules requiring lookups); runs asynchronously on repair topic or as background flow.- Batch Validator / Reconciler: periodic full-scan comparing warehouse vs raw topics for late arrivals, long-tail duplicates, metric drift.- DLQ/Repair topic & human-in-loop UI: allow fixes and replay.- Monitoring/Alerting: track schema registry changes; counts of rejected records, dedupe rates, late-arrival tail; alert on threshold breaches and schema compatibility violations.In-stream vs in-batch checks:- In-stream (must be low-latency): deduplication within sliding window, required-field/type checks, enum validation, producer schema compatibility, rapid anomaly heuristics (e.g., value ranges).- In-batch (can be deeper): cross-partition duplicates, historical consistency checks, distributional drift detection, complex referential integrity, ML-based anomaly detection, reconciliation with warehouse, late-arrival reprocessing.Handling late-arriving events:- Use event-time processing in stream processor with watermarks; choose acceptable lateness (e.g., 5–60 min) for dedupe windows.- Buffer beyond watermarks into repair topic for very late events; batch reconciler merges late records into warehouse using idempotent upserts and merge-on-key.- Maintain watermark & watermark lag metrics to tune lateness.Deduplication approach:- Use stateful store (RocksDB in Flink) keyed by event-id with TTL equal to dedupe window. For cross-window duplicates, rely on batch reconciliation.Schema drift detection:- Schema Registry enforces strict/compatible modes. Additionally run schema-drift detector job that compares incoming Avro/JSON schemas to canonical schema and flags new optional/required fields or type changes; auto-alert on incompatible changes and allow safe evolution when compatible.Alerting & SLOs:- Define SLOs: e2e latency, % invalid records, dedupe false-positive/negatives, schema compatibility.- Alerts: immediate on incompatible schema push, high invalid rate, spike in DLQ, watermark lag > threshold.- Use metrics (counts, latencies) exported to Prometheus; alert rules to Slack/PagerDuty.Trade-offs (latency vs validation depth):- More in-stream validation increases latency and resource use (stateful ops), but prevents bad data entering warehouse. Keep critical syntactic rules in-stream; push expensive checks to async batch to keep near-real-time flow.- Longer dedupe window reduces duplicates but increases state size and memory; mitigate with hybrid: short in-stream window + batch reconciliation for long-tail.- Strict schema enforcement at producer reduces downstream complexity but requires coordination; looser policies allow producer agility at cost of more downstream validation work.Example flow:1. Message arrives → schema check against registry → pass basic checks → Flink dedupe keyed by event_id with 30-min TTL → valid → sink to warehouse (micro-batch commit) with idempotent upsert.2. Message fails heavy business rule → routed to repair topic → Validation Service/analyst reviews → corrected message replayed.3. Batch reconciler nightly compares source and warehouse, replays missing/late events.This architecture balances low-latency prevention of common errors with deeper batch validation for complex checks, while providing visibility and human-in-loop repair for edge cases.
HardTechnical
88 practiced
Given two table snapshots t1 and t2 (same schema), write an efficient SQL query that computes row-level diffs: rows added, rows removed, and rows changed with per-column change details (old_value -> new_value). Assume large tables; propose optimizations to avoid full Cartesian comparisons.
Sample Answer
Approach: use primary key (or surrogate key) to join snapshots so we avoid Cartesian. Use FULL OUTER JOIN on key to detect added/removed/possibly-changed rows; for changes, compare columns and produce per-column diff (old -> new). For large tables, pre-filter with hashing (row-level checksum) and partitioned processing.SQL (example for table t1/t2 with primary key id and columns a,b,c):Key points and optimizations:- Use FULL OUTER JOIN on primary key to avoid n^2 comparisons.- Use row-level hash/checksum to quickly skip identical rows; hashes are much cheaper than per-column comparisons.- For very large tables: process incrementally by partitioning by id ranges or using change vectors (CDC), parallelize, and ensure proper indexes on id.- Use EXISTS or LEFT/ANTI joins to produce added/removed sets if FULL JOIN is expensive.- Consider Bloom filters or sampling to prefilter if keys are very numerous.Complexity: O(n) for reads and join cost depending on indexed key (external sort/hash join). Edge cases: NULL handling in hashes, type casting, collisions (use strong hash or include versioning), schema drift (columns added/removed).
sql
WITH t1_hash AS (
SELECT id, MD5(CONCAT_WS('#', a, b, c)) AS row_hash, a,b,c FROM t1
),
t2_hash AS (
SELECT id, MD5(CONCAT_WS('#', a, b, c)) AS row_hash, a,b,c FROM t2
),
joined AS (
SELECT
COALESCE(t1_hash.id, t2_hash.id) AS id,
t1_hash.row_hash AS old_hash,
t2_hash.row_hash AS new_hash,
t1_hash.a AS old_a, t2_hash.a AS new_a,
t1_hash.b AS old_b, t2_hash.b AS new_b,
t1_hash.c AS old_c, t2_hash.c AS new_c
FROM t1_hash
FULL OUTER JOIN t2_hash USING (id)
)
SELECT
id,
CASE
WHEN old_hash IS NULL THEN 'added'
WHEN new_hash IS NULL THEN 'removed'
WHEN old_hash <> new_hash THEN 'changed'
ELSE 'unchanged'
END AS change_type,
-- per-column diffs as JSON (Postgres syntax)
CASE WHEN old_hash <> new_hash THEN jsonb_strip_nulls(jsonb_build_object(
'a', CASE WHEN old_a IS DISTINCT FROM new_a THEN concat(old_a, ' -> ', new_a) END,
'b', CASE WHEN old_b IS DISTINCT FROM new_b THEN concat(old_b, ' -> ', new_b) END,
'c', CASE WHEN old_c IS DISTINCT FROM new_c THEN concat(old_c, ' -> ', new_c) END
)) END AS column_diffs
FROM joined
WHERE NOT (old_hash IS NOT NULL AND new_hash IS NOT NULL AND old_hash = new_hash);HardTechnical
90 practiced
Design an SLA-based alerting and runbook system for data quality failures. Include (1) how to pick thresholds to trigger alerts, (2) how to avoid alert fatigue, (3) sample SQL checks for critical metrics, and (4) automated remediation patterns for trivial failures (e.g., transient downstream ingestion lag).
Sample Answer
Approach: build SLA-tiered alerts (P0/P1/P2) tied to business impact, with clear thresholds chosen from historical baselines and statistical detection, plus runbooks that automate trivial fixes and route human work for true incidents.1) Picking thresholds- Map metric → business impact (e.g., downstream revenue model must have <1% missing key; else P0).- Use hybrid thresholds: hard SLAs (business rule, e.g., freshness < 15 min = fail) + statistical baselines: set dynamic thresholds at baseline ± k·std (or EWMA / MAD) and detect sustained deviations (e.g., 3 consecutive windows).- Validate thresholds with historical false-positive rate target (e.g., <5%) and adjust.2) Avoid alert fatigue- Tier alerts by severity and only page on P0/P1.- Aggregate similar alerts (dedupe by dataset/partition).- Add suppression and cool-down windows (e.g., only alert after 2 consecutive failures or 10-min sustained lag).- Provide rich context in alerts (root-cause hints, last successful run, metrics) and direct link to runbook + playbook.- Run periodic review of alert precision and retire noisy checks.3) Sample SQL checks- Row count drift:- Null rate on critical key:- Schema drift (new columns):4) Automated remediation patterns- Transient ingestion lag: detect lag < 1 hour → run automated restart/retry pipeline- Missing partition/backfill: enqueue backfill task for partition/date and monitor until success; if backfill fails 3x escalate.- Minor schema mismatch: if additive column only, auto-tag downstream features as nullable and trigger model feature regen; for breaking changes, auto-create a flagged dataset and pause dependent models.- Circuit breaker: if data quality affects model input beyond threshold, automatically switch model to cached baseline predictions and notify owners.Runbook format (for each check):- Symptoms → quick checks (last successful run, logs) → automated commands (retry, backfill URL/button, escalate) → verification steps → postmortem link.Why this works: blends business SLAs with statistical detection, reduces noisy alerts through aggregation and suppression, and resolves trivial incidents automatically so on-call focuses on high-impact issues.
sql
-- Compare today vs 7-day median
WITH today AS (SELECT COUNT(*) cnt FROM raw.my_table WHERE dt = CURRENT_DATE),
hist AS (SELECT percentile_cont(0.5) WITHIN GROUP (ORDER BY cnt) median_cnt
FROM (SELECT dt, COUNT(*) cnt FROM raw.my_table WHERE dt BETWEEN CURRENT_DATE - INTERVAL '14 days' AND CURRENT_DATE - INTERVAL '1 day' GROUP BY dt) t)
SELECT CASE WHEN today.cnt < hist.median_cnt * 0.7 THEN 'ALERT' ELSE 'OK' END status FROM today, hist;sql
SELECT SUM(CASE WHEN user_id IS NULL THEN 1 ELSE 0 END)/COUNT(*) AS null_rate
FROM raw.events WHERE dt = CURRENT_DATE;sql
SELECT column_name FROM information_schema.columns
WHERE table_name='my_table' AND column_name NOT IN (SELECT name FROM expected_schema);python
# example: restart job via orchestration API
import requests
resp = requests.post(f"{ORCH_URL}/jobs/{job_id}/run", headers={'Authorization':TOKEN})Unlock Full Question Bank
Get access to hundreds of Data Cleaning and Quality Validation in SQL interview questions and detailed answers.
Sign in to ContinueJoin thousands of developers preparing for their dream job.