Window functions help you analyze data by looking at rows related to the current row without grouping them. They let you calculate running totals, ranks, and moving averages easily.
Practical window function patterns in PostgreSQL
SELECT column1, column2, window_function() OVER (PARTITION BY columnX ORDER BY columnY ROWS BETWEEN ... ) AS alias FROM table_name;
OVER() defines the window for the function.
PARTITION BY divides rows into groups to apply the function separately.
SELECT employee_id, department_id, salary, RANK() OVER (PARTITION BY department_id ORDER BY salary DESC) AS dept_rank FROM employees;
SELECT order_id, order_date, amount, SUM(amount) OVER (ORDER BY order_date ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW) AS running_total FROM orders;
SELECT product_id, sale_date, amount, LAG(amount, 1) OVER (PARTITION BY product_id ORDER BY sale_date) AS previous_sale FROM sales;
This example creates a sales table, inserts sample data, and calculates a running total of sales amount for each product ordered by sale date.
CREATE TABLE sales ( sale_id SERIAL PRIMARY KEY, product_id INT, sale_date DATE, amount NUMERIC ); INSERT INTO sales (product_id, sale_date, amount) VALUES (1, '2024-01-01', 100), (1, '2024-01-05', 150), (1, '2024-01-10', 200), (2, '2024-01-03', 300), (2, '2024-01-07', 100); SELECT product_id, sale_date, amount, SUM(amount) OVER (PARTITION BY product_id ORDER BY sale_date ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW) AS running_total FROM sales ORDER BY product_id, sale_date;
Window functions do not reduce the number of rows returned, unlike GROUP BY.
Ordering inside OVER() is important for functions like ROW_NUMBER(), RANK(), and running totals.
Use PARTITION BY to reset calculations for each group, like per department or product.
Window functions let you perform calculations across related rows without grouping.
Common patterns include ranking, running totals, and comparing current row to previous rows.
Use PARTITION BY and ORDER BY inside OVER() to control how the window function works.
Practice
ROW_NUMBER() OVER (PARTITION BY department ORDER BY salary DESC) window function do in a query?Solution
Step 1: Understand ROW_NUMBER() with PARTITION BY
The function assigns a unique sequential number to rows within each department group.Step 2: ORDER BY salary DESC ranks highest salary first
Ordering by salary descending means the highest salary gets row number 1 in each department.Final Answer:
Assigns a unique rank to each employee within their department based on salary, starting at 1 for the highest salary. -> Option BQuick Check:
ROW_NUMBER() with PARTITION BY and ORDER BY = ranked rows [OK]
- Confusing ROW_NUMBER() with COUNT() or SUM()
- Ignoring PARTITION BY and thinking it ranks globally
- Assuming it calculates totals or averages
Solution
Step 1: Identify correct window function syntax
SUM(sales) OVER (ORDER BY date) calculates running total by ordering rows by date.Step 2: Check other options for syntax errors
The incorrect options misuse PARTITION BY without OVER(), use WHERE incorrectly, or confuse GROUP BY with window functions.Final Answer:
SELECT date, sales, SUM(sales) OVER (ORDER BY date) AS running_total FROM sales_data; -> Option DQuick Check:
SUM() OVER (ORDER BY ...) = running total syntax [OK]
- Using PARTITION BY without OVER()
- Trying to use WHERE inside window function
- Confusing GROUP BY with window functions
sales(date, amount) with rows:('2024-01-01', 100), ('2024-01-02', 150), ('2024-01-03', 200)What is the output of this query?
SELECT date, amount, SUM(amount) OVER (ORDER BY date ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW) AS running_total FROM sales ORDER BY date;
Solution
Step 1: Understand SUM() OVER with frame
The frame includes all rows from the start up to current row ordered by date, so it sums cumulatively.Step 2: Calculate running totals row by row
Row 1: 100; Row 2: 100+150=250; Row 3: 250+200=450.Final Answer:
[('2024-01-01', 100, 100), ('2024-01-02', 150, 250), ('2024-01-03', 200, 450)] -> Option CQuick Check:
Running total sums up to current row [OK]
- Ignoring frame clause and expecting total sum on all rows
- Confusing running total with simple SUM() aggregation
- Assuming NULLs appear without reason
SELECT employee_id, department, salary, RANK() OVER (ORDER BY salary DESC) AS dept_rank FROM employees;
But the query returns the same rank for all employees regardless of department. What is the likely mistake?
Solution
Step 1: Check PARTITION BY usage
If PARTITION BY is missing or incorrect, ranking happens over entire table, ignoring departments.Step 2: Confirm ORDER BY and function choice
ORDER BY DESC is correct for highest salary first; RANK() is appropriate for ties; ordering final result doesn't affect ranking.Final Answer:
Missing PARTITION BY clause inside OVER() -> Option AQuick Check:
PARTITION BY groups rows for ranking [OK]
- Forgetting PARTITION BY causes global ranking
- Confusing RANK() and ROW_NUMBER() effects
- Thinking ORDER BY outside OVER() affects ranking
Solution
Step 1: Use LAG() to get previous row's salary per department
LAG(salary) OVER (PARTITION BY department ORDER BY salary DESC) gets previous salary in descending order within department.Step 2: Subtract previous salary from current salary
salary - LAG(salary) calculates difference compared to previous employee.Final Answer:
SELECT employee_id, department, salary, salary - LAG(salary) OVER (PARTITION BY department ORDER BY salary DESC) AS salary_diff FROM employees; -> Option AQuick Check:
LAG() gets previous row value for difference [OK]
- Using LEAD() instead of LAG() for previous row
- Omitting PARTITION BY causing wrong grouping
- Trying to use aggregate functions without OVER()
Ready to prove it?
Pass the test and this pattern moves unknown
