skip to content

Window Functions

Window functions let you compute rankings, running totals, and neighbor comparisons per row without collapsing the result set. Interviewers lean on them heavily because they separate SQL users who can only GROUP BY from those who can solve real analytics problems like top-N per group in one query.

part ofSQLoverview, primer and where to startread it →
on this pageshow

questions

page 2 of 2

How do you compute a 3-day moving average of daily revenue with a window function?

level: middleimportance: should knowfreq 52%

basics

~20 s

Use AVG(revenue) OVER (ORDER BY day ROWS BETWEEN 2 PRECEDING AND CURRENT ROW). For each row the frame holds that row plus the two before it, so the average slides down the ordered rows, one row at a time.

open as a page

How do you deduplicate a table whose rows are byte-identical and that has no unique column?

level: seniorimportance: should knowfreq 28%

basics

~20 s

ROW_NUMBER can still number identical rows, but no portable DELETE can target one copy, because any predicate matching a loser matches the survivor too. Rebuild the table from the rn = 1 set and swap it in.

open as a page

In a ROW_NUMBER dedup partitioned by email, what happens to rows whose email is NULL?

level: seniorimportance: should knowfreq 32%

basics

~20 s

They all land in one partition. PARTITION BY groups NULLs together as peers rather than using = comparison, so every NULL-email row after the first is numbered above 1 and a delete of rn > 1 wipes out unrelated records.

open as a page

What does RANGE BETWEEN INTERVAL '7' DAY PRECEDING AND CURRENT ROW define as a frame?

level: seniorimportance: should knowfreq 38%

basics

~20 s

A value-based frame: every row whose ORDER BY date lies within seven days before the current row's date, through the current row and its peers. Both endpoints are inclusive, and days with no rows simply contribute nothing.

open as a page

How do you group event rows into sessions that break after 30 minutes of inactivity?

level: seniorimportance: should knowfreq 38%

basics

~20 s

Compare each row with the previous one using LAG(); emit 1 when the elapsed time exceeds the threshold and 0 otherwise, then take a running SUM() of that flag in the same order. The cumulative sum is a session id you can GROUP BY.

open as a page

A report repeats the same PARTITION BY and ORDER BY across eight window columns with different frames — how do you restructure it?

level: seniorimportance: should knowfreq 30%

basics

~20 s

Define one named window carrying the shared PARTITION BY and ORDER BY and no frame, then let each column refine it with OVER (w ROWS ...). The partition key then lives in one place, and the base must stay frame-free to remain refinable.

open as a page

Does ORDER BY inside OVER () also determine the order of the query's output rows?

level: seniorimportance: should knowfreq 50%

basics

~20 s

No. The ORDER BY inside an OVER clause only sequences rows within each partition so the window function can be computed; it makes no promise about the order rows are returned in. Only a query-level ORDER BY guarantees output order.

open as a page

NTILE(10) puts identical spend values in different deciles and boundaries move as data grows - how do you get stable segments?

level: seniorimportance: should knowfreq 32%

basics

~20 s

NTILE(10) guarantees equal-sized buckets, not equal value ranges, so identical amounts can fall either side of a boundary and cut-points drift as the population changes. For segments that depend on the value itself, bucket on explicit cut-points with a CASE expression.

open as a page

How do you return the top 3 products per category by total revenue across many order lines?

level: seniorimportance: should knowfreq 48%

basics

~20 s

Aggregate first, rank second. Sum revenue per category and product with GROUP BY, then apply ROW_NUMBER() OVER (PARTITION BY category_id ORDER BY revenue DESC) over that grouped result, and filter rn <= 3 one level further out.

open as a page

A share-of-total column using SUM() OVER () shows shares of a filtered subset, not the company total — why?

level: seniorimportance: should knowfreq 40%

basics

~20 s

Because WHERE is applied before window functions: the window only ever sees rows that survived the filter, so its total covers the filtered subset. To divide by an unfiltered total, compute the window in an inner query over all rows and filter outside it.

open as a page

How do you return each transaction with both a month-to-date and a lifetime running balance?

level: seniorimportance: should knowfreq 38%

basics

~20 s

Put two windowed SUMs in one SELECT: one partitioned by account plus the year and month of the transaction, one partitioned by account alone, both ordered by transaction time. Each OVER clause is evaluated independently over the same rows.

open as a page

In SQL, what is the gaps-and-islands problem, and what counts as an island?

level: juniorimportance: nice to knowfreq 22%

basics

~20 s

Gaps and islands names a family of SQL problems over an ordered column: islands are maximal runs of consecutive values, gaps are the stretches missing between them. The work is inventing a group key per run.

open as a page

Where does the WINDOW clause appear in a SELECT statement, and what is a named window's scope?

level: middleimportance: nice to knowfreq 24%

basics

~20 s

The WINDOW clause sits between HAVING and ORDER BY. A named window is scoped to the query block that defines it, and within that block OVER w can be used in the SELECT list and in the query's ORDER BY.

open as a page

What is the difference between PERCENT_RANK() and CUME_DIST() for tied rows?

level: middleimportance: nice to knowfreq 25%

basics

~20 s

Both give every tied row the same value. PERCENT_RANK() is (RANK() - 1) / (rows - 1), so the first row is always 0. CUME_DIST() is the fraction of rows at or before the tie group, so the last row is always 1.

open as a page

What does a QUALIFY clause do in a top-N-per-group query, in dialects that offer it?

level: middleimportance: nice to knowfreq 26%

basics

~20 s

QUALIFY filters on window function results the way HAVING filters on aggregates, so you can write the rank predicate in the same query instead of wrapping it. It is a dialect extension, not part of the ISO SQL standard.

open as a page

What do EXCLUDE CURRENT ROW and EXCLUDE TIES do in a window frame clause?

level: seniorimportance: nice to knowfreq 18%

basics

~20 s

EXCLUDE CURRENT ROW removes just the current row from the frame that was computed; EXCLUDE TIES removes its peers but keeps the current row. EXCLUDE GROUP removes both, and EXCLUDE NO OTHERS, the default, removes nothing.

open as a page

What does GROUPS BETWEEN 1 PRECEDING AND 1 FOLLOWING count in a window frame?

level: seniorimportance: nice to knowfreq 22%

basics

~20 s

Peer groups, not rows or values. The frame covers all rows tied with the current row, plus every row of the one tied group before it and the one tied group after it, however many rows each group contains.

open as a page

How do you carry the last non-NULL value forward when IGNORE NULLS is unavailable?

level: seniorimportance: nice to knowfreq 38%

basics

~20 s

Build a group id with COUNT(reading) OVER (ORDER BY ts), which stays constant across a run of NULLs, then take FIRST_VALUE(reading) OVER (PARTITION BY that id ORDER BY ts). Rows before the first value stay NULL.

open as a page

Why does COUNT(DISTINCT user_id) OVER (PARTITION BY month) fail on most engines?

level: seniorimportance: nice to knowfreq 28%

basics

~20 s

Because DISTINCT is not supported in the windowed form of an aggregate. PostgreSQL and SQL Server reject it outright, and MySQL does not support it either. Compute the distinct count in a grouped step and attach it to the detail rows instead.

open as a page

showing 31–49 of 49