SQL ORDER BY Practice
ASC and DESC are not the interview questions. The interview questions are: where do NULLs land, what does ORDER BY in a subquery actually do, how do you paginate without dropping rows under concurrent inserts, and how do you sort by a custom priority that isn't alphabetical. 6 graded problems with the seed data designed to expose each.
ASC and DESC are not the interview questions. The interview questions are: where do NULLs land, what does ORDER BY in a subquery actually do, how do you paginate without dropping rows under concurrent inserts, and how do you sort by a custom priority that isn't alphabetical. 6 graded problems with the seed data designed to expose each.
Know ORDER BY the way the interviewer who asks it knows it.
NULL placement, per engine
The defaults differ across engines, which is why every senior SQL style guide tells you to declare NULLS FIRST or NULLS LAST explicitly.
Stable pagination, with the bug and 2 fixes
-- BROKEN: returns duplicates or skips rows when 2+ posts share created_at.
-- Page 1: posts 100-91 (timestamps T..T-9)
-- New post arrives at T (same second as post 100)
-- Page 2: post 91 reappears because the inserted post shifted the offset
SELECT post_id, title, created_at
FROM posts
ORDER BY created_at DESC
LIMIT 10 OFFSET 100;
-- FIXED with deterministic tiebreaker. post_id is unique so the ordering
-- is total even when timestamps tie.
SELECT post_id, title, created_at
FROM posts
ORDER BY created_at DESC, post_id DESC
LIMIT 10 OFFSET 100;
-- BETTER, for high-traffic apps. Keyset pagination avoids OFFSET scans
-- entirely and is stable under concurrent inserts. The cursor is the
-- last (created_at, post_id) you displayed.
SELECT post_id, title, created_at
FROM posts
WHERE (created_at, post_id) < (:last_created_at, :last_post_id)
ORDER BY created_at DESC, post_id DESC
LIMIT 10;
-- Index that makes this fast:
CREATE INDEX posts_feed_idx ON posts (created_at DESC, post_id DESC);Single-column ORDER BY plus LIMIT OFFSET is the most common pagination bug. The fix is a tiebreaker or keyset pagination.
Custom priority sort with CASE
-- Sort tickets by custom priority order, not alphabetical.
-- 'high' must come first, then 'medium', then 'low'.
-- Alphabetical sort puts them in the wrong order: high, low, medium.
SELECT ticket_id, priority FROM tickets ORDER BY priority;
-- CASE-based custom sort. Each priority gets a numeric rank.
SELECT ticket_id, priority,
CASE priority
WHEN 'high' THEN 1
WHEN 'medium' THEN 2
WHEN 'low' THEN 3
END AS priority_rank
FROM tickets
ORDER BY priority_rank, created_at DESC;
-- Postgres-specific: use an array indexOf trick for the same effect.
SELECT ticket_id, priority
FROM tickets
ORDER BY array_position(ARRAY['high','medium','low'], priority),
created_at DESC;Alphabetical sorts low before medium. CASE assigns a numeric rank that matches the intent.
6 ORDER BY practice problems
Each problem targets 1 specific ORDER BY gotcha. The grader catches non-deterministic answers by running against 10 randomized seeds.
Return employees sorted by department ASC, then salary DESC within department.
ORDER BY department, salary DESC only applies DESC to salary. The default for the first column is ASC, but the grader expects you to state it explicitly.
Sort by last_sold_at DESC, with NULL (never-sold products) at the bottom regardless of engine.
Postgres puts NULLs first on DESC by default. Bare ORDER BY last_sold_at DESC sends NULLs to the top, which is the opposite of the prompt.
Return page 11 (10 posts per page) sorted by created_at DESC. Must be stable across concurrent inserts.
Seed 4 has 6 posts with identical created_at to the second. Without a tiebreaker, the same post can appear on multiple pages or vanish from all pages.
Return order_id and amount sorted by month of ordered_at DESC, then amount DESC. Do not include the month in the SELECT.
Wrapping in a subquery and naming the alias can fail on SQL Server (alias not available in ORDER BY). Repeat the expression in ORDER BY for portability.
Sort tickets by priority (high, medium, low in that order), then by created_at DESC within priority.
Alphabetical sort puts high before low before medium. CASE expression or array_position is required for the actual order.
For each user, return the 3 most recent events. Write a CTE that orders events and a wrapper that picks the top 3.
Sticking ORDER BY in the CTE does not guarantee the outer query sees events in that order. The grader runs Snowflake-strict for 1 seed; that seed returns wrong results unless ORDER BY is inside the OVER clause.
ORDER BY failure modes, by frequency
From failed submission logs across the catalog. Missing tiebreakers dominate.
ORDER BY practice FAQ
Where do NULLs land in an ORDER BY result?+
Why does my paginated query return duplicate or missing rows?+
Can I ORDER BY a column that isn't in the SELECT?+
Is ORDER BY in a subquery respected?+
Should I use ORDER BY 1, 2 (column position) or column names?+
Why is ORDER BY relevant to interview questions about ranking?+
Start with problem 3 (deterministic pagination)
- 01
Active recall beats re-reading by 50%
Cognitive-science meta-reviews (Dunlosky et al., 2013) rank practice testing as a top-tier study technique, while re-reading and highlighting rank near the bottom
- 02
76% of hiring managers reject on the coding task, not the resume
From HackerRank's 2024 Developer Skills Report. Candidates who look strong on paper still fail the live screen if they haven't done timed, executable practice
- 03
Five problem shapes cover 80% of data engineer loops
Dedup, sessionization, top-N-per-group, slowly-changing dimensions, partition tricks. Writing the shapes by hand turns the unfamiliar into pattern recognition