Window functions perform calculations across sets of query rows related to the current row without collapsing them into a single output.
Window functions let you compute running totals, rankings, moving averages, and other analytics while keeping each row visible. They simplify complex queries that would otherwise need subqueries or self-joins.
OVER() defines the “window”—the subset of rows the function can see. Use PARTITION BY to reset calculations per group and ORDER BY to set row order. Adding ROWS or RANGE refines the frame (e.g., last 3 rows).
SUM(), AVG(), COUNT(), ROW_NUMBER(), RANK(), DENSE_RANK(), LAG(), LEAD(), FIRST_VALUE(), LAST_VALUE(), and NTILE() are the workhorses for analytics, reporting, and dashboards.
Choose window functions when you need aggregated data alongside the detail rows. GROUP BY collapses rows; window functions retain them, allowing side-by-side comparison of row-level and aggregate metrics.
Partition by customer_id and sum total_amount, producing a lifetime_total column beside each order. This avoids multiple scans or joins.
Join Orders and OrderItems, aggregate by product, then apply RANK() OVER(ORDER BY sales DESC) to create a sales_rank for leaderboard style reports.
Index columns used in PARTITION BY and ORDER BY, keep frames as narrow as possible, prefer ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW for running totals, and avoid mixing GROUP BY with window functions unless necessary.
No. Use them to calculate; apply WHERE or an outer SELECT to filter on their results.
They sort partitions, so large unindexed datasets can slow down. Proper indexing and smaller partitions mitigate this.
They can because each partition is sorted. Add indexes on partition and order columns, limit frame size, and test with EXPLAIN ANALYZE.
Yes, but wrap the query in a sub-select or CTE, then apply the outer WHERE clause to the windowed column.
Frames (ROWS/RANGE) have been available since PostgreSQL 8.4; all modern versions support them.