Sorting & limiting
Concept  ·  SQL & Querying

A top-10 that should be top-10-with-ties cuts peers arbitrarily — WITH TIES / RANK is the bar.

'Top N' is ambiguous the moment values tie at position N — do you want N rows (then which peer wins needs a stated tiebreaker) or N ranks (then ties expand the list, and RANK() <= N is the tool)? LIMIT alone answers 'N rows, tiebreak by luck', the one answer nobody means. The leaderboard that shows a different bronze on every refresh is this atom plus a cache.

See it break
kites
nameheight
red90
blue80
green80
yellow60
The trap
SELECT
  name,
  height
FROM
  kites
ORDER BY
          height DESC
LIMIT
  2
;
The fix
SELECT
  name,
  height
FROM
  (
    SELECT
      name,
      height,
      RANK() OVER (
        ORDER BY
          height DESC
      ) AS rnk
    FROM
      kites
  )
WHERE
  rnk <= 2
ORDER BY
  height DESC,
  name;
The trap
red90
blue80
The fix
red90
blue80
green80

The top-2 board dropped one of the two kites tied at 80 — LIMIT 2 cuts through the tie arbitrarily, where RANK() <= 2 keeps every medalist; DuckDB rejects WITH TIES (Postgres 13+ has it).

The rule

RANK() <= N (portable, rank semantics); FETCH FIRST N ROWS WITH TIES (Postgres 13+, not DuckDB); LIMIT N with a complete ORDER BY for row semantics.

DuckDB rejects WITH TIES; Postgres 13+ has FETCH FIRST N ROWS WITH TIES; RANK() <= N is the portable bar.

Shows up elsewhere
'Latest' without a tiebreakerROW_NUMBER vs RANK vs DENSE_RANK
Try one →Prove it in practice →