PostgreSQL Window Functions

Window functions in PostgreSQL allow performing calculations across a set of table rows related to the current row, without collapsing the result set. They are powerful for analytics, ranking, running totals, moving averages, and complex reporting.

1. Basic Syntax

The basic syntax of a window function includes an aggregate or ranking function followed by an OVER() clause:

function_name([arguments]) OVER (
                [PARTITION BY partition_expression]
                [ORDER BY order_expression]
                [ROWS frame_specification]
            )

2. Common Window Functions

  • ROW_NUMBER(): Assigns a unique sequential number to rows within a partition.
  • RANK(): Assigns a rank with gaps for ties.
  • DENSE_RANK(): Assigns a rank without gaps.
  • SUM(), AVG(), MIN(), MAX(): Aggregate functions over a window.
  • LEAD() / LAG(): Access data from the next or previous row.

3. ROW_NUMBER Example

SELECT 
                id, name, department_id, salary,
                ROW_NUMBER() OVER(PARTITION BY department_id ORDER BY salary DESC) AS row_num
            FROM employees;

4. RANK and DENSE_RANK Example

SELECT 
                id, name, department_id, salary,
                RANK() OVER(PARTITION BY department_id ORDER BY salary DESC) AS rank,
                DENSE_RANK() OVER(PARTITION BY department_id ORDER BY salary DESC) AS dense_rank
            FROM employees;

5. SUM and AVG Example

SELECT 
                id, name, department_id, salary,
                SUM(salary) OVER(PARTITION BY department_id) AS total_salary,
                AVG(salary) OVER(PARTITION BY department_id) AS avg_salary
            FROM employees;

6. LEAD and LAG Example

SELECT 
                id, name, salary,
                LAG(salary, 1) OVER(ORDER BY salary) AS previous_salary,
                LEAD(salary, 1) OVER(ORDER BY salary) AS next_salary
            FROM employees;

7. Frame Specification

You can define a frame of rows relative to the current row using ROWS or RANGE in OVER().

-- Running total of salary within department
            SELECT 
                id, name, department_id, salary,
                SUM(salary) OVER(PARTITION BY department_id ORDER BY id ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW) AS running_total
            FROM employees;

8. Best Practices

  • Use window functions for analytics without collapsing rows.
  • Partition data properly to avoid incorrect aggregations.
  • Combine ORDER BY and ROWS/RANGE frames for precise calculations.
  • Avoid overusing window functions on large datasets without indexes, as it may affect performance.

Conclusion

PostgreSQL window functions provide powerful analytical capabilities for ranking, cumulative sums, moving averages, and reporting. They allow you to analyze data across rows while keeping the original row structure intact. In the next tutorial, we will explore Common Table Expressions (CTEs), which simplify complex queries and recursive operations.