Average Build Duration by Repo
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
- L3
- Asked in
- Delta Air Lines
Problem
Slow builds are blocking deploys, and the CI/CD team suspects a few repos are dragging down the pipeline. From the build log in ci_builds, show the average build duration in seconds for each repository so the team can spot the worst offenders.
Some repos build fast. Others don't.
Tables
ci_builds
Columns: build_id INTEGER, repo_name TEXT, branch TEXT, status TEXT, dur_secs INTEGER, trigger TEXT, built_at TEXT
| build_id | repo_name | branch | status | dur_secs | trigger | built_at |
|---|---|---|---|---|---|---|
| 5047 | backend-api | develop | failed | 34 | pull_request | 2026-02-02 01:13:00 |
| 5094 | infra | feature/auth | canceled | 53 | schedule | 2026-03-03 02:26:00 |
| 5141 | ml-pipeline | fix/login-bug | running | 72 | manual | 2026-04-04 03:39:00 |
| 5188 | mobile-app | release/v2.0 | queued | 91 | tag | 2026-05-05 04:52:00 |
| 5235 | data-platform | hotfix/payment | success | 110 | webhook | 2026-06-06 05:05:00 |
| 5282 | frontend | feature /spaces | failed | NULL | push | 2026-07-07 06:18:00 |
| 5329 | backend-api | main | canceled | 148 | pull_request | 2026-08-08 07:31:00 |
| 5376 | infra | develop | running | 167 | schedule | 2026-09-09 08:44:00 |
| 5423 | ml-pipeline | feature/auth | queued | 186 | manual | 2026-01-10 09:57:00 |
| 5470 | mobile-app | fix/login-bug | success | 205 | tag | 2026-02-11 10:10:00 |
Expected output
| repo_name | avg_duration |
|---|---|
| backend-api | 416.5882352941176 |
| data-platform | 458.75 |
| frontend | NULL |
| infra | 435.5882352941176 |
| ml-pipeline | 454.5882352941176 |
Worked solution and explanation
What this really is
This is a per-repo mean over a column with holes in it, dressed up as a CI slowness hunt. Anyone can write GROUP BY repo_name with AVG(dur_secs). What separates candidates is whether they trust how AVG handles missing values or try to 'fix' it. AVG leaves a missing duration out of both the sum and the count, and a repo with no recorded durations comes back with a blank average. Hand-roll SUM(dur_secs) / COUNT(*) or wrap the column in COALESCE(dur_secs, 0) and you treat a duration nobody captured as a real build. On this data that crowns 'frontend' the fastest repo in the company at 0 seconds.
Step 1: Set the grain at repo_name
One row per repository is the whole ask. branch and trigger are tempting extra columns, but each one splits a repo's average into pieces nobody asked for. A single global AVG(dur_secs) is the opposite mistake: one number and no repo to blame.
Step 2: Let AVG skip the missing durations
dur_secs is NULL on 32 of the 200 builds, and every one of them belongs to 'frontend'. AVG divides by the count of non-NULL values, so the other five repos average only what was measured, and 'frontend' returns NULL instead of an invented number. That blank row is the honest answer: the repo exists, but its durations were never captured.
Step 3: Keep every status in
status mixes 'success', 'failed', 'canceled', 'running' and 'queued' builds. The ask covers every build per repo, so there is no status filter. Keeping only successes quietly shifts every average in the result. If you think outcome matters, raise it as a question rather than adding a silent WHERE.
The solution
SELECT repo_name, AVG(dur_secs) AS avg_duration
FROM ci_builds
GROUP BY repo_name- Hand-rolled mean
SUM(dur_secs) / COUNT(*)puts rows with no duration in the denominator but not the numerator. On an INTEGER column most engines also divide as integers, so 'backend-api' reads 416 instead of 416.588.COALESCE(dur_secs, 0)is worse: 'frontend' becomes a 0-second repo.- Built-in average
AVG(dur_secs)excludes NULLs from both sides, returns a fractional mean, and leaves 'frontend' NULL. It is the same single pass with nothing to get wrong.
Where the interviewer goes next
Common follow-up questions
- How would you report the 95th percentile build duration per repo instead of the mean? (Tests whether you reach for `PERCENTILE_CONT` or `APPROX_PERCENTILE` and understand why p95 matters for CI tail latency.)
- How would you split the average by `status` so we can compare successful vs failed build times? (Probes conditional aggregation with `AVG(CASE WHEN status = 'success' THEN dur_secs END)`.)
- How would you flag repos whose average duration jumped week over week? (Tests a weekly bucket on `built_at` with `DATE_TRUNC` and `LAG` for the week-over-week delta.)