Relix

Problem solving

The most, the top N, the latest

Grain: one row per top thing (or per key, for the latest) · Class: Ranking · Signals: the most, top N, highest, latest, best per, runner-up · Operators: TOP, WINDOW RANK/ROW_NUMBER/DENSE_RANK, ARGMAX

The problem

"What are the two best-selling products? The best seller in each region? The latest price for each product? And when two products tie for best, do I want one of them or both?"

How to recognise it

The question says the most, the highest, top N, latest, best per, Nth. It is about position in an order, not a total — so the answer is a row, carrying its columns, not an aggregated number. That is what separates ranking from summary: how much did the top region sell is a γ; which region sold the most is a rank.

The choice of operator is really a choice about what you want back, and about ties:

The data

Sales := [
| region | product | units |
|--------|---------|-------|
| North  | yo-yo   | 30    |
| North  | kite    | 30    |
| North  | drum    | 10    |
| South  | atlas   | 20    |
| South  | globe   | 25    |
];

North has a tie: yo-yo and kite both sold 30. That tie is the interesting part.

Recipe 1: the top rows (TOP)

TOP n <sort> keeps the top n rows; PER restarts the count per group. It returns whole rows.

Query
query { TOP 2 units DESC (Sales) };
query { TOP 1 units DESC PER region (Sales) };
Result
 region  product  units
 ──────  ───────  ─────
 North   yo-yo       30
 North   kite        30
(2 rows)
 region  product  units
 ──────  ───────  ─────
 North   yo-yo       30
 South   globe       25
(2 rows)

Note that TOP 1 PER region returns one North row even though two are tied — it keeps a fixed number of rows, and breaks the tie for you. Whether that is what you want is Recipe 3.

Recipe 2: one value from the top row (ARGMAX)

When you want a single field of the top row — its name, its id — and one row per group, ARGMAX(rank, yield) reads "rank by the first, return the second". It lives inside a γ, so it composes with other aggregates.

Query
query { γ region, ARGMAX(units, product) → top_product, MAX(units) → best (Sales) };
Result
 region  top_product  best
 ──────  ───────────  ────
 North   yo-yo          30
 South   globe          25
(2 rows)

MAX(units) gives the number; ARGMAX(units, product) gives which product achieved it — the row-with-the-maximum lookup SQL needs a window or a self-join for.

Recipe 3: rank every row, then decide (WINDOW RANK)

TOP 1 decides the tie for you. When you want both tied leaders, rank every row and filter — RANK() gives tied rows the same rank, so rnk ≤ 1 keeps them all:

Query
Ranked := { WINDOW RANK() SORT units DESC PER region AS rnk (Sales) };
query { σ rnk ≤ 1 (Ranked) };
Result
 region  product  units  rnk
 ──────  ───────  ─────  ───
 North   yo-yo       30    1
 North   kite        30    1
 South   globe       25    1
(3 rows)

The three ranking functions differ only on ties, and the difference is the whole point of choosing between them:

Recipe 4: the latest per key (ROW_NUMBER)

Latest per key is a ranking in disguise: number each key's rows newest-first, keep number 1, drop the number. This is the SQL ROW_NUMBER() OVER (PARTITION BY … ORDER BY …) = 1 idiom.

Query
PriceHistory := [
| product | at         | price |
|---------|------------|-------|
| kite    | 2026-06-01 | 8     |
| kite    | 2026-06-03 | 9     |
| kite    | 2026-06-02 | 7     |
| drum    | 2026-06-05 | 20    |
];

Latest := { WINDOW ROW_NUMBER() SORT at DESC PER product AS rn (PriceHistory) };
query { π product, at, price (σ rn = 1 (Latest)) };
Result
 product  at          price
 ───────  ──────────  ─────
 kite     2026-06-03      9
 drum     2026-06-05     20
(2 rows)

Use ROW_NUMBER here, not RANK: you want exactly one row per key even if two share a timestamp, so an arbitrary tie-break is the right behaviour.

Variations

Pitfalls

Check it