Window functions perform calculations across a set of related rows without collapsing the result set, enabling running totals, rankings, and other analytics.
Window functions calculate running totals, rankings, and moving averages while keeping every source row visible. They eliminate the need for self-joins or subqueries for many analytics tasks.
Each window function ends with OVER()
.Inside, PARTITION BY
defines row groups, ORDER BY
sets row sequence, and an optional frame clause (ROWS
or RANGE
) limits the window.
function_name (expression)
OVER (
[PARTITION BY col_list]
[ORDER BY col_list]
[ROWS|RANGE BETWEEN frame_start AND frame_end]
)
Ranking: ROW_NUMBER()
, RANK()
, DENSE_RANK()
, NTILE()
. Aggregates: SUM()
, AVG()
, MIN()
, MAX()
, COUNT()
with OVER()
.Offset: LAG()
, LEAD()
.
SELECT c.id,
c.name,
SUM(o.total_amount) OVER(PARTITION BY c.id) AS total_spent,
RANK() OVER(ORDER BY SUM(o.total_amount) DESC) AS spend_rank
FROM Customers c
JOIN Orders o ON o.customer_id = c.id;
The query shows each customer’s total spend while still listing individual orders and ranks customers by spend.
Yes. Each column can use a different OVER()
definition.SQL Server evaluates them independently, letting you mix running totals with rankings in a single SELECT.
Always specify ORDER BY
for deterministic results. Index partition and order columns to lower sorting cost. For readability, place complex windows in CROSS APPLY
or CTEs.
Use GROUP BY
instead when you only need aggregated rows.Avoid very wide windows on large datasets without proper indexing—they may require costly sorts.
Window functions require sorting, but with proper indexes they perform similarly. Without indexes, they can be slower than a grouped aggregate.
Use a subquery, CTE, or WITH TIES
clause.SELECT aliases are not visible to WHERE in the same query block.
When ORDER BY
is present, SQL Server defaults to RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW
; otherwise the entire partition is used.
.
Both need to scan data, but window functions add a sort. Proper indexes can make them comparable; lack of indexing slows windows.
No, not in the same query block. Use a subquery or CTE to reference the calculated column in an outer WHERE clause.
If ORDER BY is present, SQL Server assumes RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW. Without ORDER BY, the entire partition is used.