skip to content

OVER Clause Anatomy

The OVER clause is the grammar of every window function: how PARTITION BY splits rows, how ORDER BY sequences them, and where a frame fits in. Interviewers ask you to read or write an OVER clause aloud to check you understand what each part does — and why a window function can't appear in WHERE.

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

questions

4

What do PARTITION BY and ORDER BY do inside a window function's OVER clause?

level: juniorimportance: must knowfreq 80%

answer

  1. Three optional slots inside the parentheses
  2. One slot splits, another sequences
  3. Think group-like split, then in-group sort
  4. Leave both out and everything is one group
  5. Split, sequence, then narrow with a frame

basics

~20 s

PARTITION BY splits the rows into independent groups so the function restarts in each one; ORDER BY sequences the rows inside a partition so position-dependent functions and frames have a defined order. Both parts are optional.

solid answer

~50 s

`OVER` is the clause that turns an ordinary function call into a window function, and it has three optional slots: `PARTITION BY`, `ORDER BY`, and a frame. `PARTITION BY expr` divides the rows the query produced into groups by that expression; the window function is computed separately in each group and restarts at every new partition. It does **not** remove rows and does not collapse them — every input row still comes out. `ORDER BY expr` sequences the rows *within* each partition. It matters for anything positional (numbering, offsets) and it is what makes a frame meaningful, because a frame is expressed relative to the current row's position. Omit `PARTITION BY` and the whole result set is one partition. Omit `ORDER BY` and the rows in a partition have no defined sequence, so the function sees the partition as an unordered set.

code

sql · 6 lines
sql
SELECT employee_id,
       department_id,
       salary,
       AVG(salary) OVER (PARTITION BY department_id) AS dept_avg,
       ROW_NUMBER() OVER (PARTITION BY department_id ORDER BY salary DESC) AS pay_rank
FROM employees;

go deeper

for a junior

Be ready to read an OVER clause aloud and say what each part does: PARTITION BY splits rows into groups, ORDER BY sequences rows inside a group. Know that both are optional and that no rows are removed.

for a middle

Explain why the window's ORDER BY exists at all — it is what gives positional functions and frames a defined notion of preceding and following — and state that the window only ever sees rows that survived WHERE, GROUP BY and HAVING.

for a senior

Show judgment about which partition key actually answers the business question, and be able to spot a window whose missing ORDER BY makes a positional result nondeterministic in a report someone depends on.

for a principal

Own the readability argument: a query with several near-identical OVER clauses is a maintenance hazard, and standardising how teams write and factor window definitions keeps analytical SQL reviewable.

## What OVER is A window function is an ordinary function call followed by the keyword `OVER` and a window definition in parentheses. The `OVER` clause is what makes it a *window* function: it tells the engine which other rows this row is allowed to see when the value is computed. Without `OVER` the same name means something else entirely — `SUM(amount)` is a grouped aggregate that collapses rows, `SUM(amount) OVER (...)` is a window aggregate that leaves every row in place and attaches a computed value to it. The window definition has three optional parts, always in this order: ```sql function_name(args) OVER ( PARTITION BY partition_expr [, ...] -- 1. split ORDER BY sort_expr [ASC|DESC] [, ...] -- 2. sequence frame_clause -- 3. narrow ) ``` ## PARTITION BY: splitting into independent groups `PARTITION BY` takes one or more expressions and divides the rows into groups that share the same value of those expressions. The function is then evaluated independently inside each group, and its state resets at every partition boundary. In `SUM(amount) OVER (PARTITION BY customer_id)`, each row gets the total for *its own* customer. Two things it is not: - It is **not a filter**. It never removes rows the way `WHERE` does; it only decides which rows are peers of which. - It does **not collapse** rows. Ten input rows produce ten output rows, each carrying its partition's value. The partition expressions are ordinary expressions, not just bare columns — `PARTITION BY EXTRACT(YEAR FROM order_date)` is legal. Multiple keys work like a compound grouping key: `PARTITION BY region, product_id` makes one partition per (region, product) pair. ## ORDER BY: sequencing rows inside a partition `ORDER BY` inside `OVER` gives the rows of each partition an order. That order is what lets the engine answer "which row is before this one". It is essential for anything positional — numbering rows, looking at the previous row's value, accumulating a total as you walk forward. It is also what makes a frame meaningful, because a frame is defined relative to the current row's position in that sequence: with no sequence there is no "preceding" and no "following". Crucially, this `ORDER BY` is scoped to the window. It orders rows *for the computation*; it says nothing about the order in which the query hands rows back to the client. That is the job of the query's own `ORDER BY` clause, which is a separate thing that happens to share a keyword. ## The frame slot The third slot narrows the window further, from "the whole partition" to a moving span around the current row, written with `ROWS`, `RANGE` or `GROUPS` (for example `ROWS BETWEEN 2 PRECEDING AND CURRENT ROW`). It only becomes meaningful once `ORDER BY` has sequenced the rows, and most queries leave it out and take the default. ## Both parts are optional — four shapes ```sql OVER () -- one unordered partition: the whole result set OVER (PARTITION BY dept_id) -- per-department, unordered OVER (ORDER BY hire_date) -- one partition, sequenced by date OVER (PARTITION BY dept_id ORDER BY hire_date) -- per-department, sequenced by date ``` All four are valid SQL. Which one you want depends on the question: "restart per group?" chooses `PARTITION BY`, "does position matter?" chooses `ORDER BY`. ## Reading one aloud A useful interview habit is to narrate the clause left to right. `AVG(score) OVER (PARTITION BY class_id ORDER BY exam_date ROWS BETWEEN 2 PRECEDING AND CURRENT ROW)` reads as: *for each class separately, walking exams oldest to newest, average the score over this exam and the two before it.* Split → sequence → narrow. If you can say that sentence, you can read any window clause. ## Where the window sits in the query The rows a window sees are the rows that survived `FROM`, `WHERE`, `GROUP BY` and `HAVING` — a window never sees rows the filters removed. That is why `AVG(salary) OVER ()` in a query with `WHERE department_id = 3` is the average of department 3, not of the whole table. ## Common mistakes Treating `PARTITION BY` as a synonym for `GROUP BY` (it splits but does not collapse); assuming both slots are required (`OVER ()` is legal); and assuming the window's `ORDER BY` sorts the output. Each of those is a distinct misunderstanding of one slot.

  • Can PARTITION BY take an expression instead of a bare column?
    Yes. Any expression over the row's columns works, so `PARTITION BY EXTRACT(YEAR FROM order_date)` gives one partition per calendar year, and `PARTITION BY region, product_id` partitions on a compound key. The rule is the same as for grouping expressions: rows with equal values of the expression land in the same partition.
  • If OVER (PARTITION BY dept_id) has no ORDER BY, what order do rows have inside a partition?
    None that you may rely on. The partition is an unordered set, so order-insensitive functions such as `SUM`, `AVG`, `MIN` and `MAX` give a stable answer, while position-dependent ones such as `ROW_NUMBER`, `LAG` and `LEAD` have no defined basis for choosing a first or previous row. Add a window `ORDER BY` whenever position matters.
  • Does PARTITION BY reduce the number of rows the query returns?
    No. A window function is row-preserving: every row that reached the window stage comes out, with an extra computed column attached. `PARTITION BY` only decides which rows are peers for that computation. If you want one row per group instead, that is `GROUP BY`, which is a different construct.

PARTITION BY is dealing one deck into piles by suit; ORDER BY is arranging each pile by rank. The function then walks each pile on its own, from the top.

saying these in an interview costs you the question

  • Says PARTITION BY filters rows the way WHERE does
  • Thinks PARTITION BY collapses each group to one row
  • Believes ORDER BY inside OVER sorts the final output
  • Assumes both PARTITION BY and ORDER BY are mandatory
  • Cannot tell the window's frame apart from its ORDER BY

context

open as a page

Why can't a window function appear in WHERE, and how do you filter on one?

level: middleimportance: must knowfreq 75%

basics

~20 s

Window functions are computed after WHERE, GROUP BY and HAVING have run, so their values do not exist yet at filter time; the standard allows them only in the SELECT list and the query's ORDER BY. To filter on one, compute it in a subquery or CTE and filter in the outer query.

open as a page

What does an empty OVER () with no PARTITION BY or ORDER BY compute?

level: middleimportance: should knowfreq 55%

basics

~20 s

OVER () treats the entire result set surviving WHERE, GROUP BY and HAVING as one unordered partition covering every row, so SUM(amount) OVER () returns the grand total repeated on every output row, and COUNT(*) OVER () returns the total row count.

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