Window functions

The SQL feature that separates juniors from seniors. Running totals, rankings, lag/lead — without grouping the rows away.

⏱ 10 min read Advanced SQL Updated May 2026

What you'll learn

  • The mental model — "aggregate without collapsing"
  • PARTITION BY, ORDER BY, and frame clauses
  • The 5 ranking and offset functions you'll use weekly
Prerequisites: sql/select-basics , sql/aggregates

A normal aggregate (SUM, AVG, COUNT) collapses many rows into one. A window function computes an aggregate over a “window” of rows without collapsing — every input row gets one output row.

The syntax adds OVER (...) after the function:

SUM(amount) OVER (PARTITION BY user_id ORDER BY date)

Three pieces:

  • PARTITION BY — split the rows into independent groups (like GROUP BY, but the rows stay).
  • ORDER BY — order rows within each partition.
  • The frame — optional; defines which rows around the current row to include.

Try it

The seed schema has orders joined to users and products. Let’s compute the running total revenue per country:

SQL · SQLite
Ready · ⌘↵ runs
Result
(click Run)

The same SUM you’ve used before — now scoped to each country and running through time. No GROUP BY needed, and you get one row per order.

Rankings — ROW_NUMBER, RANK, DENSE_RANK

SQL · SQLite
Ready · ⌘↵ runs
Result
(click Run)

The differences:

FunctionWhat happens on ties
ROW_NUMBEREach row gets a unique sequential number (ties broken arbitrarily).
RANKTied rows get the same rank; the next rank skips ahead (1, 2, 2, 4).
DENSE_RANKTied rows get the same rank; next rank doesn’t skip (1, 2, 2, 3).

Use ROW_NUMBER when you want to deduplicate. Use RANK / DENSE_RANK when ties should share a position.

LAG and LEAD — the “previous row” superpower

SQL · SQLite
Ready · ⌘↵ runs
Result
(click Run)

LAG(col) gives the value of col from the previous row (within the partition, in order). LEAD(col) gives it from the next row. Combine with date math and you get day-over-day deltas, gap analysis, churn windows — the bread and butter of analytics.

Top-N per group, the full pattern

SQL · SQLite
Ready · ⌘↵ runs
Result
(click Run)

The reason for the outer query: window functions are evaluated after the WHERE clause of the same SELECT, so you can’t filter on them inline. Wrap-and-filter is the idiom.

Quick check

Quick check

0/3 answered
Q1.What's the difference between a window function and an aggregate function?
Q2.Three users tied for the top score. Which function gives them ranks (1, 1, 1, **4**)?
Q3.You want the top 3 customers per region. What's the cleanest approach?

Finished the lesson?

Mark it complete to track your progress and keep your streak alive. +20 XP

Skip to content