Data Problem Solving and Business Context Questions
Practical data oriented problem solving that connects business questions to correct, robust analyses. Includes translating business questions into queries and metric definitions, designing SQL or query logic for edge cases, handling data quality issues such as nulls duplicates and inconsistent dates, validating assumptions, and producing metrics like retention and churn. Emphasizes building queries and pipelines that are resilient to real world data issues, thinking through measurement definitions, and linking data findings to business implications and possible next steps.
HardTechnical
31 practiced
Explain how to compute churn using survival analysis (Kaplan-Meier estimator) versus naive churn rate. Provide SQL or pseudocode to compute Kaplan-Meier on user activity data, explain how to handle censored observations (users with limited follow-up), and give an example interpretation where survival analysis offers deeper insight.
Sample Answer
Approach — Why survival analysis vs naive churn:- Naive churn rate = (churned users during period) / (users at start). It's simple but biased when users have different follow-up times or when observation window ends (right-censoring).- Kaplan‑Meier (KM) estimates the survival function S(t) = P(user still active at time t) taking censoring into account. It gives time-varying retention curves and median/percentile survival times.Key definitions:- Event = churn (e.g., first gap of >= X days or explicit cancellation).- Time = days (or weeks) from cohort start to event or censoring.- Censoring = user still active or lost to follow-up before event by study end.Pseudocode / SQL sketch (build time-to-event per user, then KM table):Compute KM survival iteratively (in SQL or in BI tool code):S(0)=1; for each t>0: S(t) = S(t-1) * (1 - d_t / n_t)Handling censored observations:- Users with no churn event before study_end are right-censored at their last observed time. They contribute to n_at_risk up to censoring time but never to d_events. KM properly accounts for them so survival probabilities are not artificially deflated.Example interpretation:- Naive 30-day churn: 20% of users churned in month 1.- KM shows S(30)=0.82 (18% churn by day 30) but S(90)=0.60 — reveals heavy mid-term attrition. KM also shows median survival = 120 days (50% retention), which helps prioritize interventions at ~90–120 days. KM can compare cohorts (A/B, acquisition channels) with log‑rank tests to detect significant retention differences.Practical tips:- Choose event definition carefully (inactivity gap vs explicit cancel).- Truncate very long tails or small-risk groups; smooth or use confidence intervals.- Implement KM calc in SQL for n_t and d_t, then finalize product outside DB or with windowed multiplication.
sql
-- 1) Define cohort start per user (e.g., signup_date)
WITH events AS (
SELECT user_id, signup_date,
MAX(last_activity_date) AS last_activity,
-- compute days until churn based on gap rule: churn_date = first date where gap >=30
MIN(activity_date) FILTER (WHERE gap_days >= 30) AS churn_date
FROM user_activity_with_gaps
GROUP BY user_id, signup_date
),
times AS (
SELECT
user_id,
DATE_PART('day', COALESCE(churn_date, last_activity, study_end) - signup_date) AS time,
CASE WHEN churn_date IS NOT NULL AND churn_date <= study_end THEN 1 ELSE 0 END AS event
FROM events
)
-- 2) KM table: for each integer t compute n_at_risk and d_events
SELECT
t,
SUM(CASE WHEN time >= t THEN 1 ELSE 0 END) AS n_at_risk,
SUM(CASE WHEN time = t AND event = 1 THEN 1 ELSE 0 END) AS d_events
FROM generate_series(0, max_time) AS t
CROSS JOIN times
GROUP BY t
ORDER BY t;EasyTechnical
25 practiced
You find 10% of rows in the payments table have null revenue_amount. As the BI analyst owning monthly revenue reporting, outline a step-by-step investigation plan and SQL checks you would run to determine root cause. For each potential root cause propose a treatment in reporting (treat as zero, exclude, impute, or backfill) and explain the business implications of each choice.
Sample Answer
Investigation plan (step-by-step) with SQL checks:1) Quantify & profile nulls- Count nulls, distribution by date, payment method, region, user, and source system:2) Time correlation- Check if nulls spike at specific ingest times or deployments:3) Upstream source & ETL- Identify source system / batch id for null rows:- Compare raw source table values to see if nulls originate upstream.4) Business logic & joins- Check if revenue derived via joins (e.g., price * qty) and join failures cause nulls:5) Data type / casting / rounding errors- Search for non-numeric values or failed cast logs in ETL.6) User / fraudulent / test transactions- Filter by test_user flag, status, or tiny amounts:7) Recent fixes / backfills- Check if backfill jobs exist and whether they completed.Potential root causes, recommended treatment, and business implications:A) Upstream missing value (source empty)- Treatment: Exclude from reported revenue until backfilled.- Implication: Revenue understated; communicate gap to stakeholders and track backfill; avoids inflating revenue with assumptions.B) Legitimately zero (refunds or free trials)- Treatment: Treat as zero.- Implication: Accurately reflects revenue impact; confirm business rule that these records should be zero, update ETL to set 0.C) Join/ETL bug (should be computed)- Treatment: Backfill (recompute from source/price/qty) and fix ETL.- Implication: Restores correct historical revenue; temporary reporting lag until backfill completes.D) Temporary latency / delayed settlement (will arrive later)- Treatment: Impute for near-term reporting (e.g., use prior-day average) but mark as provisional; later replace with actuals.- Implication: Gives more stable trends but adds estimation risk; must label and track revisions.E) Fraud/test data- Treatment: Exclude from revenue.- Implication: Prevents noise; ensure filters consistently applied.Operational next steps:- Log decisions, add monitoring alert for >X% nulls, add upstream SLA with owners, and implement ETL validation to set defaults or fail fast. Communicate chosen treatment and expected revision policy to stakeholders.
sql
SELECT
COUNT(*) AS total_rows,
SUM(CASE WHEN revenue_amount IS NULL THEN 1 ELSE 0 END) AS null_count,
ROUND(100.0 * SUM(CASE WHEN revenue_amount IS NULL THEN 1 ELSE 0 END)/COUNT(*),2) AS pct_null
FROM payments;sql
SELECT payment_date, payment_method, COUNT(*) AS nulls
FROM payments
WHERE revenue_amount IS NULL
GROUP BY 1,2
ORDER BY nulls DESC
LIMIT 50;sql
SELECT DATE(created_at) dt, COUNT(*) nulls FROM payments
WHERE revenue_amount IS NULL
GROUP BY 1 ORDER BY 1;sql
SELECT source_system, etl_batch_id, COUNT(*) FROM payments
WHERE revenue_amount IS NULL GROUP BY 1,2;sql
SELECT p.id
FROM payments p
LEFT JOIN products pr ON p.product_id = pr.id
WHERE p.revenue_amount IS NULL AND pr.price IS NULL
LIMIT 10;sql
SELECT status, COUNT(*) FROM payments WHERE revenue_amount IS NULL GROUP BY status;HardSystem Design
21 practiced
For a fact table of 5B rows across 3 years where queries commonly filter by date range and user_id, propose a partitioning and clustering strategy for Snowflake, BigQuery, or Redshift. Discuss trade-offs for query performance, storage cost, write latency, compaction, and handling of late-arriving data and updates.
Sample Answer
Requirements & constraints (clarify):- Fact table: ~5B rows spanning 3 years.- Common query patterns: filter by date range (time series) and user_id (per-user analytics).- Must balance query latency (dashboard interactivity), storage cost, ingestion throughput, and support late-arriving data/updates.High-level strategy (per engine):- Snowflake: - Partitioning: Snowflake uses micro-partitions automatically; don’t create manual date partitions. Rely on natural ingestion order by date. - Clustering: Define a cluster key on (date_trunc('day', event_date), user_id) or composite (event_date, user_id). Keep event_date first so date-range pruning is efficient, user_id second to speed selective user queries. - Maintenance: Enable automatic/online reclustering if available (or run RECLUSTER jobs during off-peak). Monitor clustering depth and pruning metrics.- BigQuery: - Partitioning: Partition the table by event_date (DAY). Use ingestion-time partitions only for streaming if needed, but event_date is preferred for accurate pruning. - Clustering: Cluster on user_id (and possibly event_type). BigQuery will colocate rows within partitions to accelerate selective user filters and reduce scanned bytes.- Redshift: - Partitioning: Use date-based sort key (compound sort key: event_date DESC, user_id) if most queries use ranges plus user_id. For concurrency and joins on users, consider distribution key = user_id to colocate data for joins. - Maintenance: Regular VACUUM/ANALYZE and VACUUM DELETE/REINDEX; for large append-heavy workloads, use SORTKEY maintenance windows.Trade-offs & effects1) Query performance:- Date-first strategies maximize partition pruning: time-range scans read far fewer micro-partitions/partitions → low scanned data and fast response for dashboards.- Clustering by user_id reduces IO for selective user queries (single user or small set). If queries are broad (full date rollups), clustering adds little benefit.- In Redshift, choosing distkey=user_id speeds joins on user dimension but can skew if a few users produce a disproportionate amount of rows.2) Storage cost:- BigQuery charges per bytes scanned; partitioning + clustering reduces bytes scanned and thus cost.- Snowflake charges for storage and compute; clustering increases compute (reclustering) and possibly transient storage for reclustered copies — monitor cost vs latency benefit.- Redshift storage cost is fixed, but poor sort keys increase query CPU and I/O; vacuuming can temporarily increase storage.3) Write latency & ingestion:- Appends by time are cheap in all three. Streaming late-arriving records that target older partitions require: - BigQuery: streaming inserts to historical partitions are allowed but can increase cost; consider batch load to partition files. - Snowflake: micro-partitions will get rewritten during reclustering; immediate reads are fine, but reclustering may be deferred. - Redshift: updates/inserts into old sort order increase fragmentation; triggers more frequent VACUUM which delays ingestion throughput.- If high ingestion concurrency matters, prefer append-only partition strategy with buffered staging (nearline staging table) and periodic bulk merges into production partition.4) Compaction & maintenance:- Snowflake: automatic micro-partition re-clustering is available (but costs compute). Manual RECLUSTER or CREATE TABLE AS SELECT can defragment.- BigQuery: background optimization is automatic; clustering effectiveness can degrade over time—re-cluster by rewriting partitions if needed.- Redshift: VACUUM/ANALYZE needed frequently; expensive for large tables. Use SORTKEY design and periodic full compaction windows.5) Late-arriving data & updates:- Pattern: write late rows to a staging table partitioned by event_date; run periodic MERGE/overwrite jobs per affected partition/day.- Snowflake: MERGE is efficient; consider swapping partitions via transient table + rename to minimize time window.- BigQuery: Use partition-level overwrite jobs (WRITE_TRUNCATE per partition) or MERGE for incremental updates. For streaming inserts, set a window to backfill and then rewrite partition when stable.- Redshift: Use staging + COPY + DELETE/MERGE (via INSERT+UPDATE or use Amazon Redshift Spectrum to replace partition files) to minimize VACUUM pressure.Recommendations (practical BI-focused):- Primary partitioning by event_date (day) so dashboard date filters prune aggressively.- Secondary clustering/sort on user_id to accelerate per-user queries common in dashboards.- Ingestion pattern: ephemeral staging (same schema) → bulk upserts per-day partition during low usage (hourly/daily). Avoid frequent single-row updates.- Monitor: partition pruning efficiency (bytes scanned), clustering depth, skew on distribution key, maintenance compute costs.- Automation: schedule recluster/rewrites for "hot" partitions weekly; implement alerts when query scanned bytes or latency rises.When to change strategy:- If per-user queries dominate and user cardinality is low/very skewed: consider distribution/salting strategies to avoid hotspots.- If late-arriving data is frequent and small: optimize MERGE pipelines and accept higher compute cost for frequent small rewrites or move to append-only with correction rows and logic in BI metrics.Summary:Partition by date (day) for pruning; cluster/sort by user_id to speed selective user filters. Use staging + bulk MERGE for late-arrivals and minimize frequent small updates. Balance reclustering/vacuum costs against dashboard latency by scheduling maintenance during off-peak and monitoring scanned bytes and query latency to tune.
MediumTechnical
28 practiced
Write SQL to compute 90-day LTV for install cohorts defined by install_date. Use payments(user_id, amount, paid_at) and installs(user_id, install_date). LTV is average revenue per user in the cohort within 90 days of install. Handle users with multiple payments and exclude test/internal accounts if given a flag.
Sample Answer
Approach: For each user, sum payments that occurred within 90 days after their install (inclusive). Then compute cohort LTV as the average of per-user 90-day revenue (this naturally counts users with zero revenue). Exclude test/internal users when a flag is set.Key points:- Use LEFT JOIN so users with no payments contribute zero to the average.- Aggregate payments per user first to avoid double-counting users with multiple payments.- Excluding test users is handled at the install set; alternatively filter payments table if tests can have payments without installs.Edge cases:- Users with multiple installs — choose deduplication rule (this assumes one install row per user; if multiple, pick first_install with MIN(install_date)).- Timezones: ensure paid_at and install_date are comparable datetimes.- Large datasets: consider partitioning or pre-aggregating payments for performance.
sql
-- Params:
-- :exclude_tests boolean (true = exclude test/internal accounts)
-- test_users table (optional) with user_id for internal/test accounts
WITH installs_filtered AS (
SELECT i.user_id, i.install_date
FROM installs i
WHERE NOT (:exclude_tests = true AND EXISTS (
SELECT 1 FROM test_users t WHERE t.user_id = i.user_id
))
),
user_revenue AS (
-- sum payments per user occurring within 90 days of their install
SELECT
f.user_id,
f.install_date::date AS cohort_date,
COALESCE(SUM(p.amount), 0) AS revenue_90d
FROM installs_filtered f
LEFT JOIN payments p
ON p.user_id = f.user_id
AND p.paid_at >= f.install_date
AND p.paid_at < f.install_date + INTERVAL '90 days'
GROUP BY f.user_id, cohort_date
)
-- cohort LTV: average revenue per user in cohort within 90 days
SELECT
cohort_date AS install_date,
COUNT(*) AS users_in_cohort,
SUM(revenue_90d) AS total_revenue_90d,
ROUND(AVG(revenue_90d)::numeric, 2) AS ltv_90d
FROM user_revenue
GROUP BY cohort_date
ORDER BY cohort_date;HardSystem Design
25 practiced
Design an anomaly detection pipeline for key business metrics that handles seasonality and weekday effects, allows detection at global/product/region levels, provides explainability for alerts, and maintains low false positive rates. Describe statistical methods (e.g., EWMA, STL decomposition, control charts), data pipeline components, latency, and how to validate and tune thresholds.
Sample Answer
Requirements & constraints:- Detect anomalies for metrics with seasonality + weekday effects, across hierarchy (global/product/region), low false positives, explainable alerts, near-real-time where needed (e.g., hourly) and daily for business KPIs.High-level architecture:- Ingestion: CDC/stream (Kafka) + batch (daily ETL) into a time-series store (BigQuery/Redshift/Timescale).- Aggregation layer: pre-compute hourly/daily rollups per dimension (product, region, global).- Modeling layer: stat models + control logic in a compute cluster (Airflow + Python).- Alerting & UI: alert service (Pager/Slack/email) + BI dashboards (Looker/Tableau) showing context and explanations.- Feedback loop: label store for human feedback, retraining/tuning job.Statistical methods:- Decompose series with STL (seasonal-trend decomposition) to remove weekly/daily seasonality; work on residuals for detection.- Baselines: use rolling-median and robust local regression (LOESS) for trend adaptation.- Detection algorithms: - EWMA on residuals for small persistent shifts (lambda tuned to sensitivity). - Control charts: Shewhart for large point anomalies, CUSUM/EWMA-control for gradual shifts. - Hierarchical detection: run detection bottom-up and top-down. Suppress parent alerts if child-level explains change (e.g., product spike explains global).- Seasonality/weekday handling: include day-of-week and holiday regressors; separate models per weekday bucket or include seasonal component in STL.Explainability:- Present STL components (trend, seasonal, residual) and show which component deviated.- Feature attribution: correlate residuals with correlated metrics (traffic, campaigns, inventory) via short-window Granger-ish tests, cross-correlation, or an LIME-like local linear model.- Show contributing dimensions: top N products/regions by delta, percent of global impact.Latency & scale:- Hourly pipeline for operational metrics (end-to-end ~5–15 minutes after window close).- Daily pipeline for strategic KPIs (overnight).- Use incremental computations and pre-aggregations to keep cost/latency low.Validation & threshold tuning:- Backtest on historical labeled incidents: compute precision, recall, F1, mean time to detect.- Use cost-based thresholds: set thresholds to minimize expected cost = FP_cost * FP_rate + FN_cost * FN_rate.- Calibrate using ROC/PR curves per metric; choose operating point matching stakeholder risk tolerance.- Use holdout windows and time-series cross-validation (rolling origin) to avoid leakage.- Implement adaptive thresholds: percentile-based dynamic thresholds (e.g., 99th residual quantile) with hysteresis and suppression windows to reduce flapping.Reducing false positives:- Require multi-signal corroboration (e.g., same anomaly seen in related metric or in multiple regions).- Use ensemble voting (Shewhart OR EWMA OR CUSUM) with majority or weighted votes.- Debounce alerts: require persistence for N consecutive windows or a minimum relative/absolute delta.Operational considerations:- Alert metadata: severity, likely root cause, confidence score, affected entities, suggested next steps.- Monitoring & metrics on detection system: alert volume, FP rate (from feedback), latency.- Governance: regular review cadence with stakeholders to adjust sensitivity, add holiday calendars, and label incidents.This pipeline balances statistical rigor (STL, EWMA, control charts), hierarchical logic for multi-level detection, explainability via decomposition & attribution, and practical tuning/validation to keep false positives low while delivering actionable alerts in BI dashboards.
Unlock Full Question Bank
Get access to hundreds of Data Problem Solving and Business Context interview questions and detailed answers.
Sign in to ContinueJoin thousands of developers preparing for their dream job.