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.

Prepare for the interview
01 / Open invite
02min.

Know ORDER BY the way the interviewer who asks it knows it.

a ORDER BY query, the same shape a screen would give you.
The diff against expected. Where ties broke. What you missed.
sandbox
1SELECT user_id,
2 COUNT(*) AS sessions
3FROM events
4WHERE ts >= NOW() - INTERVAL '7 day'
5
Execute your solution0.4s avg.
StripeInterview question
Solve a ORDER BY problem

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.

Engine
ORDER BY x ASC
ORDER BY x DESC
NULLS FIRST / LAST supported
Override syntax
Postgres
NULL last
NULL first
Yes
NULLS FIRST | LAST
MySQL 8+
NULL first
NULL last
Yes (8.0+)
NULLS FIRST | LAST
SQL Server
NULL first
NULL last
No
Use CASE WHEN x IS NULL
Oracle
NULL last
NULL first
Yes
NULLS FIRST | LAST
Snowflake
NULL last
NULL first
Yes
NULLS FIRST | LAST
BigQuery
NULL first
NULL last
Yes
NULLS FIRST | LAST

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.

01Multi-column with mixed direction
Easy
employees(emp_id, department, salary)

Return employees sorted by department ASC, then salary DESC within department.

Trap

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.

Per-column direction
02NULL placement, portable
Easy
products(product_id, name, last_sold_at)

Sort by last_sold_at DESC, with NULL (never-sold products) at the bottom regardless of engine.

Trap

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.

NULLS FIRST / NULLS LAST
03Deterministic pagination
Medium
posts(post_id, created_at, title)

Return page 11 (10 posts per page) sorted by created_at DESC. Must be stable across concurrent inserts.

Trap

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.

Tiebreaker column, LIMIT/OFFSET correctness
04ORDER BY a computed expression
Medium
orders(order_id, amount, ordered_at)

Return order_id and amount sorted by month of ordered_at DESC, then amount DESC. Do not include the month in the SELECT.

Trap

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.

ORDER BY expression, alias scope by dialect
05CASE-based custom priority
Medium-Hard
tickets(ticket_id, priority, created_at)

Sort tickets by priority (high, medium, low in that order), then by created_at DESC within priority.

Trap

Alphabetical sort puts high before low before medium. CASE expression or array_position is required for the actual order.

CASE-based ordering, custom enum sort
06ORDER BY in subquery is not binding
Hard
events(event_id, user_id, event_at)

For each user, return the 3 most recent events. Write a CTE that orders events and a wrapper that picks the top 3.

Trap

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.

Window function ordering, subquery ordering semantics

ORDER BY failure modes, by frequency

From failed submission logs across the catalog. Missing tiebreakers dominate.

Most common ORDER BY failure modes in submission logs (n = 8,420 failed submissions)
Missing tiebreaker on ORDER BY in window function
38%
Wrong NULL placement (engine default differs from prompt)
24%
Alphabetical sort on enum (priority, status, tier)
14%
ORDER BY in subquery, expected to bind in outer query
11%
LIMIT without ORDER BY at all
8%
Other (column position, expression issues)
5%

ORDER BY practice FAQ

Where do NULLs land in an ORDER BY result?+
Depends on the engine and the direction. The table above shows the defaults across Postgres, MySQL 8+, SQL Server, Oracle, Snowflake, and BigQuery. The portable answer is to declare NULLS FIRST or NULLS LAST explicitly whenever the placement matters.
Why does my paginated query return duplicate or missing rows?+
Non-deterministic ORDER BY. If multiple rows share the value you sort by, LIMIT OFFSET can return overlapping sets on consecutive pages or skip rows under concurrent inserts. Fix: add a tiebreaker column (typically the primary key) at the end of ORDER BY. For high-traffic apps, switch to keyset pagination, which doesn't rely on OFFSET.
Can I ORDER BY a column that isn't in the SELECT?+
Yes in most engines. Postgres, MySQL, Snowflake, BigQuery, Oracle allow ORDER BY on any expression valid in the FROM clause. SQL Server is stricter when DISTINCT is used. Including the column in SELECT is the safe portable choice.
Is ORDER BY in a subquery respected?+
Standards say no. ORDER BY in a subquery or CTE has no binding effect on the outer query unless paired with LIMIT or FETCH. Postgres often preserves the order in practice; Snowflake explicitly does not. For window functions, place ORDER BY inside the OVER clause; for the final result, ORDER BY at the outermost level.
Should I use ORDER BY 1, 2 (column position) or column names?+
Column names for production and interview code. Position works but silently breaks when SELECT is reordered during refactoring. The exception is 1-off ad-hoc queries, where position is faster to type and the cost of a wrong sort is low.
Why is ORDER BY relevant to interview questions about ranking?+
Every ROW_NUMBER, RANK, and DENSE_RANK reads ORDER BY inside OVER. If the ORDER BY is not total (no tiebreaker), the ranking is non-deterministic and the dedup or top-N answer fails under multi-seed grading. ORDER BY hygiene is the upstream skill for half of window function correctness.
02 / Why practice

Start with problem 3 (deterministic pagination)

  1. 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

  2. 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

  3. 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

Adjacent topics