SQL window functions perform calculations across a set of rows related to the current row — without collapsing them into a single group like GROUP BY does. They are essential for analytics: ranking, running totals, moving averages, and accessing previous or next row values in a single query.
SQLWindow FunctionsAnalyticsDatabaseSELECT name, department, salary, ROW_NUMBER() OVER (PARTITION BY department ORDER BY salary DESC) AS row_num FROM employees;
SELECT name, score, RANK() OVER (ORDER BY score DESC) AS rank FROM leaderboard;
SELECT name, score, DENSE_RANK() OVER (ORDER BY score DESC) AS dense_rank FROM leaderboard;
SELECT * FROM (
SELECT name, department, salary,
ROW_NUMBER() OVER (PARTITION BY department ORDER BY salary DESC) AS rn
FROM employees
) ranked
WHERE rn <= 3;
SELECT date, revenue, LAG(revenue, 1) OVER (ORDER BY date) AS prev_revenue, revenue - LAG(revenue, 1) OVER (ORDER BY date) AS day_over_day FROM daily_sales;
SELECT date, revenue, LEAD(revenue, 1) OVER (ORDER BY date) AS next_revenue FROM daily_sales;
SELECT name, salary, department, FIRST_VALUE(salary) OVER (PARTITION BY department ORDER BY salary DESC) AS highest_in_dept FROM employees;
SELECT date, revenue,
SUM(revenue) OVER (ORDER BY date
ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW) AS running_total
FROM daily_sales;
ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW means "all rows from the start through the current row". This is the standard cumulative sum pattern for financial dashboards.SELECT date, revenue,
AVG(revenue) OVER (
ORDER BY date
ROWS BETWEEN 6 PRECEDING AND CURRENT ROW
) AS moving_avg_7d
FROM daily_sales;
ROWS BETWEEN 6 PRECEDING AND CURRENT ROW creates a 7-row window (6 before + current). Widely used in time-series analysis and financial charting to show trends.SELECT name, department, salary, ROUND(100.0 * salary / SUM(salary) OVER (PARTITION BY department), 2) AS pct_of_dept FROM employees;
GROUP BY collapses multiple rows into one row per group, removing individual row details. Window functions perform calculations across rows but return a value for every row, preserving all original rows. Use GROUP BY for aggregated summaries; use window functions when you need both the detail rows and an aggregate value.
No — window functions are evaluated after WHERE and cannot be used directly in WHERE, GROUP BY, or HAVING clauses. Wrap the query in a subquery or CTE and filter in the outer query: SELECT * FROM (SELECT ..., ROW_NUMBER() OVER(...) rn FROM ...) t WHERE rn = 1.
Yes, all major databases support them: PostgreSQL (since 8.4), MySQL (since 8.0), SQL Server (since 2005), Oracle (since 8i), and SQLite (since 3.25). If you're on an older MySQL version (5.x), you'll need to use workarounds with user variables or subqueries.