The Slow Creep

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
ESPN

Problem

The platform team is checking whether CI builds have crept slower across the years in each repository. Give the average build duration per repository and calendar year, slowest average first, breaking ties by repository name alphabetically and then from the earliest year.

Every year the builds got heavier. Find where the minutes went.

Tables

ci_builds

Columns: build_id INTEGER, repo_name TEXT, branch TEXT, status TEXT, dur_secs INTEGER, trigger TEXT, built_at TEXT

ci_builds: 10 of 200 rows
build_idrepo_namebranchstatusdur_secstriggerbuilt_at
5047backend-apidevelopfailed34pull_request2026-02-02 01:13:00
5094infrafeature/authcanceled53schedule2026-03-03 02:26:00
5141ml-pipelinefix/login-bugrunning72manual2026-04-04 03:39:00
5188mobile-apprelease/v2.0queued91tag2026-05-05 04:52:00
5235data-platformhotfix/paymentsuccess110webhook2026-06-06 05:05:00
5282frontendfeature /spacesfailedNULLpush2026-07-07 06:18:00
5329backend-apimaincanceled148pull_request2026-08-08 07:31:00
5376infradeveloprunning167schedule2026-09-09 08:44:00
5423ml-pipelinefeature/authqueued186manual2026-01-10 09:57:00
5470mobile-appfix/login-bugsuccess205tag2026-02-11 10:10:00

Expected output

First 5 of 30 rows
repo_namebuild_yearavg_duration
data-platform2023608
mobile-app2022589
infra2025551
backend-api2024532
ml-pipeline2026471.9

Worked solution and explanation

What this problem really is

Under the CI-metrics costume this is a two-dimensional grouping problem with a sorting twist. Anyone can group by repo and pull AVG(dur_secs); what actually separates candidates is two quieter things. First, prying the calendar year out of a TEXT timestamp without reaching for EXTRACT, which the SQLite sandbox rejects outright. Second, making the slowest-first ordering deterministic. Skip the tie-breaker and your rows reshuffle whenever two averages collide, and a grader that diffs exact row order fails a query that is arithmetically perfect.


Break down the requirements

Step 1: Pull the year from a text timestamp

built_at is stored as TEXT, not a native timestamp, so reach for STRFTIME('%Y', built_at) to get the four-digit year. This is the SQLite move; EXTRACT(YEAR FROM ...) is the Postgres habit that crashes the sandbox.

Step 2: Average duration per repo per year

Group on the pair (repo_name, derived year) and take AVG(dur_secs). Grouping on both dimensions is the whole point: drop either and you have answered a different question.

Step 3: Sort slowest to fastest, deterministically

Order by avg_duration DESC to put the slowest averages on top, then add repo_name and build_year as tie-breakers so equal averages always land in the same order across runs.


The solution

Multi-dimension aggregation sorted by performance
SELECT
    repo_name,
    STRFTIME('%Y', built_at) AS build_year,
    AVG(dur_secs) AS avg_duration
FROM ci_builds
GROUP BY repo_name, STRFTIME('%Y', built_at)
ORDER BY avg_duration DESC, repo_name, build_year

Common follow-up questions