How to Use Window Functions in PostgreSQL

Galaxy Glossary

How do I use window functions in PostgreSQL?

Window functions perform calculations across related query rows without collapsing them into groups.

Sign up for the latest in SQL knowledge from the Galaxy Team!
Thank you! Your submission has been received!
Oops! Something went wrong while submitting the form.

Description

How to Use Window Functions in PostgreSQL

Window functions add analytic power to SELECT statements by computing running totals, rankings, moving averages, and more without losing row-level detail.

What are PostgreSQL window functions?

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.

Why choose window functions over GROUP BY?

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.

How do I write a basic window function?

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.

Example: running total per customer

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;

What frame clauses can I use?

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).

Which built-in functions work as window functions?

Most aggregates (SUM, AVG, COUNT, MIN, MAX) and specialized ones like ROW_NUMBER(), RANK(), DENSE_RANK(), LAG(), LEAD(), FIRST_VALUE(), LAST_VALUE(), NTH_VALUE().

Can I reuse a window definition?

Yes. Use WINDOW clause: WINDOW w AS (PARTITION BY ...ORDER BY ...) then SUM(amount) OVER w to keep SQL DRY.

Best practices for window functions?

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.

Common mistakes and fixes

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.

Further examples

Top-N per category

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;

Month-over-month difference

SELECT month,
revenue,
revenue - LAG(revenue) OVER (ORDER BY month) AS mom_change
FROM monthly_revenue;
.

Why How to Use Window Functions in PostgreSQL is important

How to Use Window Functions in PostgreSQL Example Usage


SELECT employee_id,
       department_id,
       salary,
       AVG(salary) OVER (PARTITION BY department_id) AS avg_dept_salary
FROM   employees;

How to Use Window Functions in PostgreSQL Syntax


<window_function> OVER ( [PARTITION BY expr_list] [ORDER BY expr_list] [<frame_clause>] )

frame_clause ::= {ROWS | RANGE} BETWEEN <frame_start> AND <frame_end>
frame_start/end ::= UNBOUNDED PRECEDING | <n> PRECEDING | CURRENT ROW | <n> FOLLOWING | UNBOUNDED FOLLOWING

WINDOW window_name AS (PARTITION BY ... ORDER BY ... [frame_clause])

Common Mistakes

Frequently Asked Questions (FAQs)

Can I filter on a window function result?

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.

Do window functions slow queries?

They can if partitions are large. Add indexes on PARTITION BY and ORDER BY columns, limit frame size, and avoid unnecessary columns.

Can I combine GROUP BY and window functions?

Yes. Perform GROUP BY in a subquery, then apply window functions outside if you need both aggregated and analytic results together.

Want to learn about other SQL terms?