The Quiet Middle

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
Spotify

Problem

We're auditing the batch pipeline and want the jobs in the overlooked middle by rows processed: not the busiest, not the idlest. Line them up from most rows to least, then return positions 8 through 10, each job's name with its position, lowest position first.

Not the biggest, not the smallest. The overlooked middle.

Tables

batch_jobs

Columns: job_id INTEGER, job_name TEXT, status TEXT, rows_done INTEGER, started TEXT, ended TEXT, 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 6 rows
job_namernk
data_cleanup8
data_cleanup8
user_export9
user_export9
sync_elastic10

Worked solution and explanation

What this really is

This is a question about what a position means, dressed up as a pipeline audit. Every rows_done value in batch_jobs is held by two runs, so positions 8 through 10 are six rows, not three: two data_cleanup runs at 8,924 rows, two user_export runs at 8,827 and two sync_elastic runs at 8,633. Anyone can sort by rows processed. What separates candidates is reading position as a standing shared by every run with the same count and numbered without gaps. Slice physical rows with LIMIT 3 OFFSET 7 instead and you get one metric_agg run and two cache_warm runs, none of which belong in the band.

One number per row, or gaps after ties
ROW_NUMBER() splits the 9,409-row pair across positions 7 and 8 and puts the 9,215-row pair at 9 and 10, so the band is metric_agg, cache_warm, cache_warm. RANK() lets each pair share but skips a number after it (1, 1, 3, 3, ...), so the only position inside 8 to 10 is 9, held by the cache_warm pair.
Shared, gap-free positions
DENSE_RANK() gives both runs of a count the same position and the next lower count the very next number, so 8, 9 and 10 are the 8,924, 8,827 and 8,633 counts: six runs, exactly the preview.

Build it in layers

Step 1: Number by the count, not by the row

Compute DENSE_RANK() OVER (ORDER BY rows_done DESC) as rnk. Ordering by the count descending is the 'most rows to least' in the statement, and the dense numbering is what the preview shows: two rows at 8, two at 9, then 10.

Step 2: Filter the position one level up

A window is evaluated after WHERE, so WHERE DENSE_RANK() OVER (...) BETWEEN 8 AND 10 is rejected outright. Put the numbering in a derived table (ranked) and filter its rnk column from the outer query.

Step 3: Keep the band, lowest position first

rnk BETWEEN 8 AND 10 is inclusive at both ends, which is exactly the three positions asked for. ORDER BY rnk puts position 8 first. The two runs sharing a position are identical rows, so nothing more is needed to order them.

Positions computed inside, band filtered outside
SELECT job_name, rnk
FROM (
    SELECT job_name, DENSE_RANK() OVER (ORDER BY rows_done DESC) AS rnk
    FROM batch_jobs
) ranked
WHERE rnk BETWEEN 8 AND 10
ORDER BY rnk

Common follow-up questions