The Runs That Never Ended
A medium SQL interview practice problem on DataDriven. Write the query and run it against a real database, with instant feedback.
- Domain
- SQL
- Difficulty
- medium
- Seniority
- L3
- Asked in
- eBay
Problem
Each row of ml_models is one training run of a model, and a run that produced no score has no accuracy recorded. Report each model's average accuracy alongside its completion rate, the percentage of its runs that recorded an accuracy.
Some models learn. Some just stop answering. Find out which ones finish what they start.
Tables
ml_models
Columns: model_id INTEGER, mdl_name TEXT, version TEXT, accuracy REAL, status TEXT, train_at TEXT, framework TEXT
| model_id | mdl_name | version | accuracy | status | train_at | framework |
|---|---|---|---|---|---|---|
| 123 | rec_engine | v1.1 | 0.72 | training | 2026-02-02 01:00:00 | TensorFlow |
| 146 | churn_pred | v2.0 | 0.89 | archived | 2026-03-03 02:00:00 | XGBoost |
| 169 | spam_filter | 3.0 | 0.61 | failed | 2026-04-04 03:00:00 | sklearn |
| 192 | img_classify | v2.1-beta | 0.78 | validating | 2026-05-05 04:00:00 | pytorch |
| 215 | sentiment | 1.0.0 | 0.95 | Deployed | 2026-06-06 05:00:00 | tensorflow |
| 238 | demand_fcst | v1.0 | 0.67 | deployed | NULL | PyTorch |
| 261 | price_opt | v1.1 | 0.84 | Training | 2026-08-08 07:00:00 | TensorFlow |
| 284 | fraud_detect | v2.0 | NULL | archived | 2026-09-09 08:00:00 | XGBoost |
| 307 | rec_engine | 3.0 | 0.73 | failed | 2026-01-10 09:00:00 | sklearn |
| 330 | churn_pred | v2.1-beta | 0.9 | validating | 2026-02-11 10:00:00 | pytorch |
Expected output
| mdl_name | avg_accuracy | completion_rate |
|---|---|---|
| churn_pred | 0.8808 | 100 |
| demand_fcst | 0.725 | 100 |
| fraud_detect | NULL | 0 |
| img_classify | 0.84 | 100 |
| price_opt | 0.895 | 100 |
Worked solution and explanation
This is a data-completeness audit dressed up as model evaluation. One column, accuracy, has to answer two questions: how good is a model when a run produced a score, and how often did a run produce one at all. Anyone writes GROUP BY mdl_name with AVG(accuracy). What separates candidates is treating NULL as a run that never finished, not a run that scored zero. You let the aggregates' NULL-skipping do the work: AVG and COUNT(accuracy) ignore NULLs, and COUNT(*) does not. Put COUNT(*) in the numerator and 'fraud_detect', whose every run is NULL, reports 100% complete. The one model this report exists to flag comes out looking perfect.
One column, two statistics
Step 1: Group to one row per mdl_name
model_id identifies a training run, not a model. 'rec_engine' shows up under several versions and ids. The output grain is the model name, so GROUP BY mdl_name and nothing else. Adding version would split each model into fragments.
Step 2: Let AVG(accuracy) skip the NULLs
AVG divides the sum of non-null values by the count of non-null values. That is exactly the mean over finished runs. Don't wrap the column in COALESCE(accuracy, 0): that invents zero-accuracy runs and drags down the average of any model with a missing score.
Step 3: Divide COUNT(accuracy) by COUNT(*)
Same column, two counts. COUNT(accuracy) counts only runs with a score, so it is the numerator. COUNT(*) counts every run, so it is the denominator. Multiply by 100.0 before dividing so the ratio is computed in floating point.
Step 4: Round the average last
Wrap the finished AVG(accuracy) in ROUND(..., 4). Rounding an intermediate value bakes the error into everything computed from it. The completion rate is reported exactly as the division produces it.
SELECT mdl_name,
ROUND(AVG(accuracy), 4) AS avg_accuracy,
COUNT(accuracy) * 100.0 / COUNT(*) AS completion_rate
FROM ml_models
GROUP BY mdl_name- What candidates write
COUNT(*) * 100 / COUNT(*)is 100 for every model.AVG(COALESCE(accuracy, 0))counts missing runs as zero-accuracy runs. 'fraud_detect' comes out at 100% complete with an average of 0.- What the data needs
COUNT(accuracy) * 100.0 / COUNT(*)measures finished runs against all runs.AVG(accuracy)averages only real scores. 'fraud_detect' comes out at 0.0 complete with a NULL average, which is the honest answer.
Where it breaks
Common follow-up questions
- Should a run with `status` 'failed' but a non-null `accuracy` count as complete? (Tests whether you question the proxy: non-null `accuracy` stands in for completion, but `status` may disagree with it.)
- `status` mixes 'Deployed' and 'deployed'. How would you add a completion rate per status? (Tests normalizing with `LOWER(status)` before grouping instead of trusting raw values.)
- Rank models by `completion_rate`, breaking ties by `avg_accuracy`. Where do NULL averages go? (Tests `ORDER BY ... DESC NULLS LAST` and awareness that engines sort NULL differently by default.)