Window functions perform calculations across related query rows without collapsing them into groups.
Window functions add analytic power to SELECT statements by computing running totals, rankings, moving averages, and more without losing row-level detail.
Window functions are aggregate-like functions that operate over a specified “window” of rows returned by the query.Unlike GROUP BY, they return one value per input row, keeping detail intact.
Use window functions when you need both row detail and aggregates in the same result set—e.g., showing each order with its customer’s running total.
Place the function in the SELECT list and add an OVER() clause. Specify PARTITION BY to reset calculations per group and ORDER BY to define row order.Frame clauses refine the window.
SELECT customer_id,
order_date,
amount,
SUM(amount) OVER (PARTITION BY customer_id ORDER BY order_date
ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW) AS running_total
FROM orders
ORDER BY customer_id, order_date;
ROWS or RANGE BETWEEN defines how many rows around the current one are included.Common shortcuts: ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW (running), ROWS BETWEEN 1 PRECEDING AND 1 FOLLOWING (moving window).
Most aggregates (SUM, AVG, COUNT, MIN, MAX) and specialized ones like ROW_NUMBER(), RANK(), DENSE_RANK(), LAG(), LEAD(), FIRST_VALUE(), LAST_VALUE(), NTH_VALUE().
Yes. Use WINDOW clause: WINDOW w AS (PARTITION BY ...ORDER BY ...)
then SUM(amount) OVER w
to keep SQL DRY.
Index columns in PARTITION BY and ORDER BY for speed, keep frames explicit for readability, and avoid mixing windowed and non-windowed aggregates without GROUP BY.
Omitting ORDER BY causes nondeterministic results in ranking or running totals. Always set ORDER BY when order matters.
Forgetting frame clause nuances can yield unexpected rows.Remember RANGE uses logical values, while ROWS uses physical row counts.
SELECT *
FROM (
SELECT product_id, category_id, sales,
ROW_NUMBER() OVER (PARTITION BY category_id ORDER BY sales DESC) AS rn
FROM product_sales
) sub
WHERE rn <= 3;
SELECT month,
.
revenue,
revenue - LAG(revenue) OVER (ORDER BY month) AS mom_change
FROM monthly_revenue;
Yes, but wrap the query as a subquery or CTE because window functions cannot appear in WHERE. Use the outer query’s WHERE to filter.
They can if partitions are large. Add indexes on PARTITION BY and ORDER BY columns, limit frame size, and avoid unnecessary columns.
Yes. Perform GROUP BY in a subquery, then apply window functions outside if you need both aggregated and analytic results together.