SQL Window Functions — ROW_NUMBER, RANK, LAG, LEAD

Comprehensive guide to SQL window functions. Covers ROW_NUMBER, RANK, DENSE_RANK, LAG, LEAD, running totals, and window frame specifications.

Advanced SQL

Detailed Explanation

Window Functions Explained

Window functions perform calculations across a set of rows related to the current row without collapsing them into a single output row (unlike GROUP BY).

Syntax

function_name() OVER (
  [PARTITION BY col1, col2]
  [ORDER BY col3 ASC|DESC]
  [frame_clause]
)

Ranking Functions

SELECT name, department_id, salary,
  ROW_NUMBER() OVER (ORDER BY salary DESC) AS row_num,
  RANK()       OVER (ORDER BY salary DESC) AS rank,
  DENSE_RANK() OVER (ORDER BY salary DESC) AS dense_rank
FROM employees;
Function Ties Gaps
ROW_NUMBER Each row gets a unique number No
RANK Tied rows get the same number Yes (next rank skips)
DENSE_RANK Tied rows get the same number No

Top-N Per Group

SELECT * FROM (
  SELECT name, department_id, salary,
    ROW_NUMBER() OVER (
      PARTITION BY department_id ORDER BY salary DESC
    ) AS rn
  FROM employees
) sub WHERE rn <= 3;

LAG and LEAD

SELECT month, revenue,
  LAG(revenue, 1) OVER (ORDER BY month) AS prev_month,
  LEAD(revenue, 1) OVER (ORDER BY month) AS next_month,
  revenue - LAG(revenue, 1) OVER (ORDER BY month) AS mom_change
FROM monthly_sales;

Running Totals and Moving Averages

SELECT month, revenue,
  SUM(revenue) OVER (ORDER BY month) AS running_total,
  AVG(revenue) OVER (
    ORDER BY month
    ROWS BETWEEN 2 PRECEDING AND CURRENT ROW
  ) AS moving_avg_3m
FROM monthly_sales;

Availability

  • PostgreSQL: Full support since 8.4
  • MySQL: Full support since 8.0 (not available in 5.7)
  • SQLite: Full support since 3.25.0

Use Case

You need to calculate running totals, rank items within categories, compare each row to its predecessor, or find the top N records per group — all common in analytics and reporting queries.

Try It — SQL Cheat Sheet

Open full tool