One ParasolOne Parasol

Advanced SQL - Window Functions and Analytics

Perform advanced analytics with window functions and ranking

Advanced SQL - Window Functions and Analytics

📚 Resources for This Lesson

ROW_NUMBER()

Assigns a unique number to each row.

SELECT 
    first_name,
    salary,
    ROW_NUMBER() OVER (ORDER BY salary DESC) AS salary_rank
FROM employees;

RANK() and DENSE_RANK()

Ranking functions with different handling of ties.

SELECT 
    first_name,
    salary,
    RANK() OVER (ORDER BY salary DESC) AS rank,
    DENSE_RANK() OVER (ORDER BY salary DESC) AS dense_rank
FROM employees;

PARTITION BY

Divide results into groups.

-- Rank within each department
SELECT 
    first_name,
    department,
    salary,
    ROW_NUMBER() OVER (PARTITION BY department ORDER BY salary DESC) AS dept_rank
FROM employees;

LAG() and LEAD()

Access previous and next row values.

-- Compare salary with previous employee
SELECT 
    first_name,
    salary,
    LAG(salary, 1) OVER (ORDER BY hire_date) AS previous_salary,
    LEAD(salary, 1) OVER (ORDER BY hire_date) AS next_salary,
    salary - LAG(salary, 1) OVER (ORDER BY hire_date) AS salary_change
FROM employees;

Aggregate Window Functions

-- Running total
SELECT 
    order_date,
    amount,
    SUM(amount) OVER (ORDER BY order_date) AS running_total
FROM orders;

-- Comparison to average
SELECT 
    first_name,
    salary,
    AVG(salary) OVER () AS avg_salary,
    salary - AVG(salary) OVER () AS diff_from_avg
FROM employees;

FRAME Specification

-- Moving average (current + 2 previous rows)
SELECT 
    sales_date,
    sales_amount,
    AVG(sales_amount) OVER (
        ORDER BY sales_date
        ROWS BETWEEN 2 PRECEDING AND CURRENT ROW
    ) AS moving_avg
FROM daily_sales;

NTILE()

Divide data into buckets.

-- Divide employees into 4 quartiles by salary
SELECT 
    first_name,
    salary,
    NTILE(4) OVER (ORDER BY salary DESC) AS salary_quartile
FROM employees;

Practical Example

WITH employee_stats AS (
    SELECT 
        first_name,
        department,
        salary,
        RANK() OVER (PARTITION BY department ORDER BY salary DESC) AS dept_rank,
        AVG(salary) OVER (PARTITION BY department) AS dept_avg_salary,
        SUM(salary) OVER (PARTITION BY department) AS dept_total_salary
    FROM employees
)
SELECT 
    first_name,
    department,
    salary,
    dept_rank,
    dept_avg_salary,
    ROUND(salary - dept_avg_salary, 2) AS diff_from_dept_avg
FROM employee_stats
WHERE dept_rank <= 3
ORDER BY department, dept_rank;

Performance Tips

← Back to All Lessons
Copyright © 2026. Made with ♥ by OneParasol Illustrations from