Best Accuracy to Training Time Ratio

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
L4

Problem

A model's training time is its training timestamp measured in days since the Unix epoch, fractions of a day included, and a model missing either that timestamp or an accuracy score cannot be measured at all. From ml_models, find the three models delivering the least accuracy per million days of training time, lowest first, showing each one's name, accuracy, training timestamp, training days and that rate.

Fast to train. Accurate too. Which model?

Tables

ml_models

Columns: model_id INTEGER, mdl_name TEXT, version TEXT, accuracy REAL, status TEXT, train_at TEXT, framework TEXT

ml_models: 10 of 200 rows
model_idmdl_nameversionaccuracystatustrain_atframework
123rec_enginev1.10.72training2026-02-02 01:00:00TensorFlow
146churn_predv2.00.89archived2026-03-03 02:00:00XGBoost
169spam_filter3.00.61failed2026-04-04 03:00:00sklearn
192img_classifyv2.1-beta0.78validating2026-05-05 04:00:00pytorch
215sentiment1.0.00.95Deployed2026-06-06 05:00:00tensorflow
238demand_fcstv1.00.67deployedNULLPyTorch
261price_optv1.10.84Training2026-08-08 07:00:00TensorFlow
284fraud_detectv2.0NULLarchived2026-09-09 08:00:00XGBoost
307rec_engine3.00.73failed2026-01-10 09:00:00sklearn
330churn_predv2.1-beta0.9validating2026-02-11 10:00:00pytorch

Expected output

First 3 of 3 rows
mdl_nameaccuracytrain_attraining_daysaccuracy_per_million_days
sentiment0.552026-09-06 21:00:0020702.87526.5664
sentiment0.552026-01-18 21:00:0020471.87526.8661
churn_pred0.562026-09-03 02:00:0020699.08333333333227.0543

Worked solution and explanation

What this really is

This is a type-coercion problem wearing a model-quality costume. The sort is trivial; the denominator is not. train_at is text, and STRFTIME('%s', train_at) hands back text too, so dividing it by 86400 quietly becomes integer division and every training time snaps to a whole day. The second trap is quieter still: the list runs lowest first, and a NULL rate sorts ahead of every real one. Skip the null filter and your bottom three are models nobody ever scored.

Step 1: Drop the rows that cannot be measured

Filter accuracy IS NOT NULL AND train_at IS NOT NULL. Either NULL makes the rate NULL, and an ascending sort hands those rows the first slots. Both conditions matter on this data: the seed has rows missing each one.

Step 2: Turn train_at into fractional days

STRFTIME('%s', train_at) gives epoch seconds as text. Wrap it in CAST(... AS FLOAT) before dividing by 86400, so a model trained at 09:00 carries its 0.375 of a day instead of losing it.

Step 3: Use the same denominator in the rate

A SELECT cannot reference its own training_days alias, so the rate repeats the cast expression: 1000000 * accuracy / (CAST(...) / 86400). Keep the cast in both places; drop it from either and that column silently goes back to whole days. Scaling by a million keeps the values readable at four decimals.

Step 4: Lowest first, keep three

Sort on accuracy_per_million_days ascending and cut with LIMIT 3. The rates are distinct on the seed, so no tiebreaker is owed at the cut.

The three weakest models
SELECT
  mdl_name,
  accuracy,
  train_at,
  CAST(STRFTIME('%s', train_at) AS FLOAT) / 86400 AS training_days,
  ROUND(1000000 * accuracy / (CAST(STRFTIME('%s', train_at) AS FLOAT) / 86400), 4) AS accuracy_per_million_days
FROM ml_models
WHERE
  accuracy IS NOT NULL
  AND train_at IS NOT NULL
ORDER BY
  accuracy_per_million_days ASC
LIMIT 3
Raw division
STRFTIME('%s', train_at) / 86400 coerces the text to an integer and divides integer by integer. A model trained at '2026-01-10 09:00:00' lands on day 20463 flat, and every rate shifts.
Cast first
CAST(STRFTIME('%s', train_at) AS FLOAT) / 86400 keeps the fraction: day 20463.375. The query runs either way, which is exactly why the wrong one survives review.

Common follow-up questions