Average Machine Job Time

Every machine on the factory floor logs a 'start' event when it begins a job and a 'finish' event when the job is done. Job numbers restart on each machine, so job 1 on one press is unrelated to job 1 on another.

For each machine, compute the average job duration in seconds (finish time minus start time), counting only jobs that have both a start and a finish. A job that is still running has only a start event and must be ignored.

Return machine_id and avg_seconds, rounded to 2 decimal places, in any order. Machines with no finished job do not appear.

Tables

machine_events — One row per start or finish event of a job on a machine.

ColumnTypeDescription
event_id (key)INTEGERUnique id of the event
machine_idTEXTMachine that ran the job
job_noINTEGERJob number, unique only within one machine
event_typeTEXT'start' or 'finish'
at_secondINTEGERSeconds since the start of the shift when the event happened (whole number)

Example

machine_events

event_idmachine_idjob_noevent_typeat_second
1PRESS-A1start0
2PRESS-A1finish95
3PRESS-A2start120
4LATHE-11start10
5PRESS-A2finish170
6LATHE-11finish310
7PRESS-A3start200
8LATHE-12start330
9LATHE-12finish400

Expected output

machine_idavg_seconds
LATHE-1185
PRESS-A72.5

Rows may be returned in any order. Your query also runs against hidden datasets with tricky edge cases.

SQL Path · Joins · problem 18 of 70 · SQLite

Each job is spread over two rows. Put them side by side by joining the table to itself.
Match on both the machine and the job number, with one side filtered to 'start' and the other to 'finish'.
An inner join drops jobs with no finish row, which is exactly what the statement asks for. Then average per machine.

The idea

A job's duration needs two rows: its start and its finish. Joining the table to itself turns those two rows into one. The left copy s keeps only 'start' events. The right copy f must be the 'finish' event for the same machine and the same job number. Matching on the job number alone is not enough, because job numbers restart on every machine.

Each joined row is one complete job, so f.at_second - s.at_second is its duration. A job that is still running has no finish row and disappears from the inner join, so it cannot distort the average. Grouping by machine and applying AVG gives the answer. AVG always returns a real number, so ROUND(..., 2) keeps the fraction.

The alternative pivots each job into one row with conditional aggregation (MAX(CASE ...)) and then drops jobs that are missing either time.

Solution

SELECT s.machine_id,
       ROUND(AVG(f.at_second - s.at_second), 2) AS avg_seconds
FROM machine_events AS s
JOIN machine_events AS f
  ON f.machine_id = s.machine_id
 AND f.job_no = s.job_no
 AND f.event_type = 'finish'
WHERE s.event_type = 'start'
GROUP BY s.machine_id;

Another way

WITH jobs AS (
  SELECT machine_id,
         job_no,
         MAX(CASE WHEN event_type = 'start' THEN at_second END) AS started,
         MAX(CASE WHEN event_type = 'finish' THEN at_second END) AS finished
  FROM machine_events
  GROUP BY machine_id, job_no
)
SELECT machine_id,
       ROUND(AVG(finished - started), 2) AS avg_seconds
FROM jobs
WHERE started IS NOT NULL
  AND finished IS NOT NULL
GROUP BY machine_id;

Why common answers fail

Joins on the job number alone, so job 1 on one machine is paired with job 1 on every other machine.

SELECT s.machine_id, ROUND(AVG(f.at_second - s.at_second), 2) AS avg_seconds
FROM machine_events AS s
JOIN machine_events AS f
  ON f.job_no = s.job_no
 AND f.event_type = 'finish'
WHERE s.event_type = 'start'
GROUP BY s.machine_id;

Subtracts the average start from the average finish, which is only right when every job has finished; a running job shifts the start average.

SELECT machine_id,
       ROUND(AVG(CASE WHEN event_type = 'finish' THEN at_second END)
           - AVG(CASE WHEN event_type = 'start' THEN at_second END), 2) AS avg_seconds
FROM machine_events
GROUP BY machine_id;

Divides two integers, so SQLite truncates the average before rounding (an average of 1.67 becomes 1).

SELECT s.machine_id, ROUND(SUM(f.at_second - s.at_second) / COUNT(*), 2) AS avg_seconds
FROM machine_events AS s
JOIN machine_events AS f
  ON f.machine_id = s.machine_id
 AND f.job_no = s.job_no
 AND f.event_type = 'finish'
WHERE s.event_type = 'start'
GROUP BY s.machine_id;

What the interviewer is checking

Checks that you can pair related event rows with a self-join on the full key, and that you think about incomplete events before averaging.

Mistakes to avoid

  • Joining on job number only, when job numbers repeat across machines.
  • Averaging starts and finishes separately, which breaks as soon as one job is unfinished.
  • SUM / COUNT on integers, which truncates in SQLite.

Follow-ups to expect

  • Also return the longest job per machine (MAX of the same difference).
  • Include machines with no finished job showing NULL (start from a list of machines and LEFT JOIN).
  • What if a job could be started twice? (Pick the latest start before the finish, e.g. with a correlated subquery.)

In real work

Cycle-time and turnaround reporting from event logs: manufacturing jobs, support tickets, CI builds, API request traces.

Practice next

Next Post Previous Post
No Comment
Add Comment
comment url