Memra

Ranking rows

◈ 2 cards

ROW_NUMBER, RANK, and DENSE_RANK.

Number and rank

Ranking functions need an ORDER BY inside the window:

  • ROW_NUMBER() — 1,2,3… a unique position
  • RANK() — ties share a rank, then it skips (1,1,3)
  • DENSE_RANK() — ties share a rank, no gaps (1,1,2)

Most expensive book per genre — number them within each genre, priciest first:

SELECT title, genre, price,
       RANK() OVER (PARTITION BY genre ORDER BY price DESC) AS price_rank
FROM books;
titlestockrow_numberrankdense_rankRivers of Salt0111Bright Hollow0211The Tin DrumLessons2332The Long Count3443SaltwaterHymns4554RANK skips 2 after the tie. DENSE_RANK never leaves a gap.
The restock list, ORDER BY stock alone. Two books hold zero, and that tie is the only place the three functions disagree: ROW_NUMBER invents an order (which of the two gets 1 is arbitrary), RANK leaves a gap where the tie consumed a position, DENSE_RANK does not.
titlegenrepriceprice_rankRivers of Saltfiction16.001The Glass Forestfiction14.992Bright Hollowfiction13.253Quantum Morningsscience22.501Small Machinesscience18.752A new genre restarts the counter at 1.
RANK() OVER (PARTITION BY genre ORDER BY price DESC). The partition is the reset: rank 1 means "priciest in its genre", never "priciest overall".
NORMAL ~/memra/learn/postgresql/ranking utf-8 LF