skip to content

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

level: middleimportance: should knowfreq 55%

answer

  1. Both optional parts are simply absent
  2. No split means exactly one group
  3. The value is the same on every row
  4. Nothing collapses; row count is unchanged
  5. Adding a window ORDER BY changes the answer

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.

solid answer

~50 s

An empty window definition means "no split, no sequence": the whole result set is a single partition, the rows in it are unordered, and the window covers all of them. So `SUM(amount) OVER ()` puts the grand total on every row and `COUNT(*) OVER ()` puts the total row count on every row — without collapsing anything. The rows it sees are the rows that reached the window stage, meaning after `FROM`, `WHERE`, `GROUP BY` and `HAVING`. Add a filter and the "grand total" is the total of the filtered set, which is usually what you want. The moment you add `ORDER BY` inside those parentheses, the meaning changes: the rows acquire a sequence and the default window is no longer the whole partition but everything up to the current row, which is how the same `SUM` becomes a running total.

code

sql · 6 lines
sql
-- amounts 10, 20, 30 in table orders
SELECT amount,
       SUM(amount)   OVER () AS grand_total,   -- 60 on every row
       COUNT(*)      OVER () AS row_count      -- 3 on every row
FROM orders;
-- returns 3 rows, not 1

go deeper

for a junior

Remember that OVER () is valid SQL and means the whole result set as one group, so SUM(x) OVER () shows the same grand total on every row without reducing the number of rows.

for a middle

Explain the two consequences of leaving both slots out — one partition and no row sequence — and be able to contrast OVER () with OVER (ORDER BY col), which introduces a sequence and turns the same SUM into an accumulating column.

for a senior

Show you know the window sees post-WHERE, post-HAVING rows, and be ready to argue when OVER () is a cleaner way to carry a total or a result-set row count than a second query or a subquery that must repeat the filter.

for a principal

Weigh the cost side: an empty window forces the engine to materialise the whole result to produce the constant, so on very large result sets a precomputed or separately cached total may serve a dashboard better than recomputing it per request.

## The empty window `OVER ()` is a complete, legal window definition. Both of its optional parts — `PARTITION BY` and `ORDER BY` — are simply absent, and so is a frame. What is left is the simplest possible window: **one partition containing every row, in no particular order, all of it visible to the function**. ```sql SELECT order_id, amount, SUM(amount) OVER () AS grand_total FROM orders; ``` If `orders` has three rows with amounts 10, 20 and 30, this returns three rows, and every one of them carries `grand_total = 60`. Note carefully what did *not* happen: no rows were removed, and no rows were merged. A window function is row-preserving by definition, and an empty `OVER` clause does not change that. ## "One partition" and "unordered" Omitting `PARTITION BY` is the same as saying there is exactly one group. Omitting `ORDER BY` says the rows in that group have no sequence you may rely on, which in turn means positional concepts do not apply. That has a practical consequence: `OVER ()` is a sensible window for order-insensitive functions — `SUM`, `COUNT`, `AVG`, `MIN`, `MAX` — and a poor one for order-dependent functions. `ROW_NUMBER() OVER ()` is accepted by some engines but has no defined basis for deciding which row is first, so the numbering it produces is arbitrary and may differ between runs. If the numbering matters, give the window an `ORDER BY`. ## What the window covers With no `ORDER BY` and no frame, the window for every row is the entire partition. Because the partition is the entire result set, every row's window is the same set of rows, so the computed value is identical on every output row. That constancy is exactly the point: it lets you carry a whole-result-set aggregate alongside the detail rows. ## Adding ORDER BY changes the answer This is the contrast interviewers are usually probing for: ```sql SELECT order_date, amount, SUM(amount) OVER () AS total_all_rows, SUM(amount) OVER (ORDER BY order_date) AS accumulated FROM orders; ``` The first column is constant. The second grows down the result, because once the rows have a sequence the default window is no longer the whole partition but everything from the start of the partition up to the current row and its ties. Same function, same table, different window definition, completely different column. If someone hands you `SUM(x) OVER (ORDER BY d)` and expects a constant, that expectation is wrong. ## The rows it sees A window operates on the rows the query has already produced at that stage — after `FROM`, after `WHERE`, after `GROUP BY` and `HAVING`. So in: ```sql SELECT customer_id, amount, SUM(amount) OVER () AS filtered_total FROM orders WHERE order_date >= DATE '2024-01-01'; ``` `filtered_total` is the 2024 total, not the all-time total. This is almost always the desired behaviour — the aggregate matches the rows the user is looking at — but it surprises people who read `OVER ()` as "over the table". It means over the *result set*. ## Where it earns its place Three everyday uses: - **Detail plus grand total in one pass.** You get the rows and the total together, without a second query or a self-join to a total subquery. - **Share of total.** `amount * 1.0 / SUM(amount) OVER ()` gives each row's fraction of the whole result. (Watch the division: integer division and a zero denominator are the usual traps.) - **Total row count next to a page of rows.** `COUNT(*) OVER ()` returns the number of rows in the result set on every row, which is a common way to get a count for a paging UI alongside the page itself. Be aware that the count is computed over the rows that reached the window stage; if the query also applies a row limit, whether that limit has been applied yet depends on where the limit sits relative to the window in the query, so read the query carefully rather than assuming. ## Distinguishing it from the alternatives A correlated scalar subquery `(SELECT SUM(amount) FROM orders)` can produce the same constant column, but it is a separate computation over a separately specified set of rows — it does not automatically respect the outer query's `WHERE`, and you must repeat the filter to keep the two in step. `OVER ()` is defined over the result set you already have, so the two cannot drift apart. ## Gotchas worth stating aloud Empty `OVER ()` never reduces the row count; it is not shorthand for `GROUP BY` with no key; it computes over filtered rows, not the base table; and it is not the same as `OVER (ORDER BY ...)`, which introduces a sequence and, with it, a different default window.

  • Does SUM(amount) OVER () compute over the whole table or over the query's result set?
    Over the result set. The window stage sees the rows that survived `FROM`, `WHERE`, `GROUP BY` and `HAVING`, so with `WHERE order_date >= DATE '2024-01-01'` the value is the 2024 total. That is usually what you want — the aggregate stays consistent with the rows on screen — but it is not the base-table total.
  • Is ROW_NUMBER() OVER () meaningful?
    Not in any way you can depend on. With no `ORDER BY` the partition is an unordered set, so there is no defined first row and the numbering is arbitrary; it can differ between runs or plans. Order-insensitive aggregates such as `SUM` and `COUNT` are fine over an empty window, positional functions are not.
  • How is COUNT(*) OVER () different from COUNT(*) with GROUP BY?
    `COUNT(*)` with grouping collapses each group to a single output row. `COUNT(*) OVER ()` leaves every row in place and stamps the total row count onto each of them, so you keep the detail and the count in one result. Same number, completely different result shape.

saying these in an interview costs you the question

  • Thinks OVER () returns a single summary row
  • Says OVER () always aggregates the whole base table
  • Treats OVER () and OVER (ORDER BY col) as equivalent
  • Calls the empty parentheses a syntax error
  • Uses ROW_NUMBER() OVER () and expects stable numbering

context