Use ROW_NUMBER, RANK, LAG, LEAD, and aggregates over windows for powerful analytical queries.
Published April 6, 2025
Window functions perform calculations across a set of rows related to the current row — without collapsing rows like GROUP BY does. They are one of the most powerful SQL features and a frequent advanced interview topic.
function_name() OVER (
PARTITION BY column -- optional: group rows
ORDER BY column -- optional: define row order within window
ROWS/RANGE BETWEEN ... -- optional: frame specification
)
SELECT
name,
dept,
salary,
ROW_NUMBER() OVER (PARTITION BY dept ORDER BY salary DESC) AS row_num,
RANK() OVER (PARTITION BY dept ORDER BY salary DESC) AS rank,
DENSE_RANK() OVER (PARTITION BY dept ORDER BY salary DESC) AS dense_rank
FROM employees;
-- If two people tie for rank 1:
-- ROW_NUMBER: 1, 2, 3 (no ties)
-- RANK: 1, 1, 3 (skips 2)
-- DENSE_RANK: 1, 1, 2 (no gaps)
SELECT
order_date,
revenue,
LAG(revenue, 1) OVER (ORDER BY order_date) AS prev_day_revenue,
LEAD(revenue, 1) OVER (ORDER BY order_date) AS next_day_revenue,
revenue - LAG(revenue, 1) OVER (ORDER BY order_date) AS day_over_day_change
FROM daily_revenue;
SELECT
order_date,
revenue,
SUM(revenue) OVER (ORDER BY order_date
ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW
) AS running_total
FROM daily_revenue;
SELECT
order_date,
revenue,
AVG(revenue) OVER (
ORDER BY order_date
ROWS BETWEEN 6 PRECEDING AND CURRENT ROW
) AS seven_day_avg
FROM daily_revenue;
SELECT name, salary,
NTILE(4) OVER (ORDER BY salary DESC) AS quartile
FROM employees;
-- quartile 1 = top 25% earners
-- Top 2 earners per department
SELECT dept, name, salary FROM (
SELECT dept, name, salary,
ROW_NUMBER() OVER (PARTITION BY dept ORDER BY salary DESC) AS rn
FROM employees
) ranked
WHERE rn <= 2;
RANK (gaps) and DENSE_RANK (no gaps) — interviewers love asking this.LAG/LEAD are perfect for time-series comparisons — have a working example ready.PARTITION BY is optional — omitting it applies the window function across all rows.