Against the Clock

An easy SQL interview practice problem on DataDriven. Write the query and run it against a real database, with instant feedback.

Domain
SQL
Difficulty
easy
Seniority
L5
Asked in
Instacart

Problem

The on-call team's job report keeps breaking on batch_jobs rows that violate its two rules: every job should have an ended time by now, and no job should end before it started. List every job that breaks either rule with its job_id, job_name and runtime in whole minutes from started to ended.

Promised by noon. Delivered at midnight.

Tables

batch_jobs

Columns: job_id INTEGER, job_name TEXT, started TEXT, ended TEXT, status TEXT, rows_done INTEGER, priority INTEGER, retries INTEGER

batch_jobs: 10 of 200 rows
job_idjob_namestatusrows_donestartedendedpriorityretries
1047user_exportrunning972026-02-02 01:00:002026-02-02 03:11:0030
1094data_cleanupfailed1942026-03-03 02:00:002026-03-03 04:22:0060
1141email_blastqueued2912026-04-04 03:00:002026-04-04 05:33:0090
1188index_rebuildcanceled3882026-05-05 04:00:00NULL20
1235cache_warmCompleted4852026-06-06 05:00:002026-06-06 07:55:0050
1282log_archiveFAILEDNULL2026-07-07 06:00:002026-07-07 08:06:0080
1329metric_aggcompleted6792026-08-08 07:00:002026-08-08 09:17:0010
1376backup_dbrunning7762026-09-09 08:00:00NULL40
1423sync_elasticfailed8732026-01-10 09:00:002026-01-10 11:39:0075
1470daily_reportqueued9702026-02-11 10:00:002026-02-11 12:50:0000

Expected output

First 5 of 66 rows
job_idjob_nameminutes_elapsed
1188index_rebuildNULL
1376backup_dbNULL
1564data_cleanupNULL
1752log_archiveNULL
2034data_cleanup-1317.9999998956919

Worked solution and explanation

What this really tests

This is a constraint audit dressed up as a broken runtime report. The job table promises two things: every job carries an ended time, and that time never precedes started. The skill is finding the rows that break either promise and reporting their runtime. The trap is believing one test covers both. Filter on minutes_elapsed < 0 and you catch the jobs that ended before they started, but a job with no ended has a runtime of NULL, NULL is never less than zero, and all 50 stuck jobs silently drop out of the audit.


Two rules, two tests

Step 1: Test the missing end directly

ended IS NULL is the only way to ask whether a value is absent. ended = NULL is never true, and ended = '' compares against empty text the table never stores. Do not excuse 'running' or 'queued' rows by status: the report treats every job as overdue for an end time, and status does not line up with ended here anyway (some 'running' rows carry one).

Step 2: Compare the times as they are stored

started and ended are fixed-width 'yyyy-MM-dd HH:mm:ss' strings, so text order is time order and ended < started flags the end-before-start rows without converting anything. Join the two tests with OR: a row only has to break one rule to be reported.

Step 3: Convert before you subtract

Ordering survives as text; arithmetic does not. A lenient engine reads each string as its leading number, the year, so ended - started comes out 0. Cast both to TIMESTAMP, subtract to get an interval, take EXTRACT(EPOCH FROM ...) for seconds and divide by 60. A job stamped as starting at 22:00 and ending at 00:02 on the same date reports -1318.


The solution

Jobs that break either rule, with their runtime in minutes
SELECT job_id,
       job_name,
       EXTRACT(EPOCH FROM CAST(ended AS TIMESTAMP) - CAST(started AS TIMESTAMP)) / 60 AS minutes_elapsed
FROM batch_jobs
WHERE ended IS NULL
   OR ended < started
The one-test version
SELECT job_id,
       job_name,
       EXTRACT(EPOCH FROM CAST(ended AS TIMESTAMP) - CAST(started AS TIMESTAMP)) / 60 AS minutes_elapsed
FROM batch_jobs
WHERE EXTRACT(EPOCH FROM CAST(ended AS TIMESTAMP) - CAST(started AS TIMESTAMP)) < 0

Returns only the end-before-start jobs; every job with no end is missed.


Common follow-up questions