BUSINESS ANALYTICS • DATA WRANGLING AND QUERYING

Window Functions — Window function concepts (intro)

Perform calculations across related rows without collapsing your result set — unlocking rankings, running totals, and moving averages in SQL.

Historical Context & Motivation

Before window functions existed, analysts who needed to compare an individual row to an aggregate — say, comparing one quarter's revenue to the company average — faced an awkward choice. They could collapse all rows into a summary with GROUP BY, losing the row-level detail, or they could write cumbersome self-joins and correlated subqueries that were both error-prone and slow. This tension between detail-level data and aggregate insight drove the SQL standards committee and database vendors to develop a more elegant solution.

1992
SQL-92 Standard
The SQL-92 standard introduced GROUP BY and aggregate functions like SUM and AVG. These tools collapsed rows into summaries, making it impossible to retain row-level detail alongside aggregated values in a single pass.
1999
SQL:1999 — OLAP Extensions Proposed
The SQL:1999 standard introduced the OVER clause concept and the notion of analytical (window) functions. Oracle was among the first vendors to implement early versions, enabling online analytical processing (OLAP) scenarios inside the database engine itself.
2003
SQL:2003 — Full Window Function Specification
The SQL:2003 standard formalized window functions with PARTITION BY, ORDER BY, and frame specifications (ROWS BETWEEN). This became the modern foundation adopted by PostgreSQL, SQL Server, and eventually MySQL.
2012–Present
Universal Adoption
By 2012, every major relational database — including MySQL 8.0 (2018) and SQLite 3.25 (2018) — supported window functions. Cloud analytics platforms like Google BigQuery, Amazon Redshift, and Snowflake made window functions a first-class feature for business intelligence at scale.

The central question window functions answer is deceptively simple: How can we compute an aggregate or positional calculation across a set of related rows while still keeping every individual row in the result? Understanding this question — and the elegant mechanism SQL provides — is the gateway to writing sophisticated analytical queries that power dashboards, financial models, and operational reports across modern business environments.

Core Principles & Definitions

A window function performs a calculation across a set of table rows that are somehow related to the current row. Unlike an ordinary aggregate function used with GROUP BY, a window function does not cause rows to become grouped into a single output row — the rows retain their separate identities. The "window" refers to the subset of rows over which the computation occurs, and you define this window using the OVER clause. Every window function in SQL must include an OVER() clause; without it, the database treats the function as a regular aggregate.

1

The OVER Clause

The OVER() clause is the signature of a window function. It defines the "window" of rows the function should consider. An empty OVER() treats the entire result set as a single window.
2

PARTITION BY

Divides the result set into partitions (groups). The window function is applied independently within each partition. Think of it as creating logical subsets — like calculating sales rank within each region separately.
3

ORDER BY (within OVER)

Determines the logical order of rows inside each partition. This ordering is critical for ranking functions (ROW_NUMBER, RANK) and cumulative calculations like running totals.
4

Window Frame

Specifies which rows relative to the current row are included in the calculation using clauses like ROWS BETWEEN ... AND .... Frames enable moving averages, trailing sums, and other sliding-window computations.
5

No Row Collapse

The defining characteristic: window functions produce a result for every row in the original query. Unlike GROUP BY, no rows are eliminated or merged — detail and aggregate coexist side by side.
KEY TAKEAWAY
Think of a window function like a sliding glass panel on a train. As you ride through the dataset row by row, the panel frames a specific view — maybe the three rows behind you, the current row, and the two rows ahead. You calculate something about that view (an average, a rank, a cumulative sum) and record the result on the current row's ticket. The train keeps moving, the window slides, and the next row gets its own calculation. Critically, no passengers are removed from the train — every row remains in the final output.

Visual Explanation — GROUP BY vs. Window Functions

The most important distinction to internalize is how a window function preserves every row while a GROUP BY aggregation collapses them. The diagram below illustrates this side by side using a small sales dataset with four transactions across two regions.

Left side: a GROUP BY collapses four source rows into two summary rows. Right side: the same SUM function used with OVER(PARTITION BY Region) retains all four rows and appends the partition total as a new column. The dashed rectangles highlight the two partitions (East and West).

Notice how the window function result on the right-hand side preserves the granularity of the original data. Each salesperson's transaction amount remains visible, but a new column — RegionTotal — has been computed across the partition. This is extraordinarily useful in business contexts: an analyst can now calculate each transaction's percentage contribution to regional total in a single query, without needing a subquery or a separate lookup table. The detail and the aggregate coexist in the same row, which is the core value proposition of window functions.

How Window Functions Work — Anatomy of the OVER Clause

The syntax of a window function follows a consistent pattern. Every window function call consists of the function name (e.g., SUM, ROW_NUMBER, LAG), its arguments, and the OVER clause. Inside the OVER clause, three optional sub-clauses control the window's shape: PARTITION BY, ORDER BY, and the frame specification.

GENERAL WINDOW FUNCTION SYNTAX
function_name(expression) OVER ( [PARTITION BY col₁, col₂, …] [ORDER BY col₃ [ASC|DESC], …] [ROWS|RANGE BETWEEN frame_start AND frame_end] )
function_name — any aggregate (SUM, AVG, COUNT, MIN, MAX) or dedicated window function (ROW_NUMBER, RANK, DENSE_RANK, LAG, LEAD, NTILE). PARTITION BY — divides rows into independent groups; omitting it treats the entire result set as one partition. ORDER BY — determines the logical ordering within each partition; required for ranking and cumulative functions. Frame specification — fine-tunes which rows relative to the current row participate; common options include UNBOUNDED PRECEDING, CURRENT ROW, and N FOLLOWING.

Order of SQL Execution with Window Functions

Understanding when window functions execute in the SQL processing pipeline is critical. Window functions are evaluated after FROM, WHERE, GROUP BY, and HAVING have already been processed, but before the final ORDER BY and LIMIT. This means you cannot reference a window function in a WHERE clause — if you need to filter on a window function's result, you must wrap the query in a Common Table Expression (CTE) or subquery and filter in an outer query.

SQL LOGICAL EXECUTION ORDER
FROM → WHERE → GROUP BY → HAVING → SELECT (window fns here) → ORDER BY → LIMIT
Window functions are computed during the SELECT phase. Because WHERE runs earlier, filtering on a window result (e.g., keeping only rows where RANK() = 1) requires a CTE or subquery.

Categories of Window Functions

Window functions fall into three broad categories, each serving a distinct analytical purpose. Aggregate window functions apply familiar aggregation logic (sum, average, count) across the window. Ranking window functions assign ordinal positions to rows within each partition. Value/offset window functions access data from other rows relative to the current row — enabling period-over-period comparisons without self-joins.

The three families of window functions share the same OVER() clause syntax. Aggregate functions produce running or partitioned totals. Ranking functions assign positions. Value/offset functions retrieve data from neighboring rows.
Summary of window function categories and their typical business applications
CategoryFunctionsRequires ORDER BY?Typical Business Use
AggregateSUM, AVG, COUNT, MIN, MAXOptional (creates running calc when present)Running total of revenue, % of departmental spend, moving 3-month average
RankingROW_NUMBER, RANK, DENSE_RANK, NTILERequiredTop-5 salespeople per region, quartile buckets for customer spending
Value / OffsetLAG, LEAD, FIRST_VALUE, LAST_VALUERequiredMonth-over-month revenue change, comparing each sale to the first sale in a cohort

Worked Example — Ranking Quarterly Revenue by Department

Imagine you are a business analyst at a retail company. Management wants a report showing each department's quarterly revenue alongside its rank within the company, and each department's revenue as a percentage of the company's total. We will build this query step by step using window functions, working from the following dept_revenue table for Q4 2024.

dept_revenue table — Q4 2024
deptq4_revenue
Electronics520,000
Apparel310,000
Home Goods275,000
Grocery480,000
Sports190,000
Building a Revenue Ranking & Percentage Report
1
Step 1 — Write the Base SELECTStart with the columns we want to display: dept and q4_revenue. We will add computed window columns next.
SELECT dept, q4_revenue FROM dept_revenue;
2
Step 2 — Add a Ranking Column with RANK()We add RANK() OVER (ORDER BY q4_revenue DESC) to assign rank 1 to the highest-revenue department. Because there is no PARTITION BY, the window covers the entire result set — all five departments compete in one ranking.
RANK() OVER (ORDER BY q4_revenue DESC) AS revenue_rank
3
Step 3 — Compute Percentage of Total with SUM() OVER()To find each department's share of company-wide revenue, divide its q4_revenue by the grand total. We obtain the grand total using SUM(q4_revenue) OVER () — the empty OVER() means "sum across all rows." Multiply by 100 and round to get a clean percentage.
ROUND(100.0 * q4_revenue / SUM(q4_revenue) OVER (), 1) AS pct_of_total
4
Step 4 — Add a Running TotalFor an optional running total (cumulative revenue when sorted by rank), use SUM(q4_revenue) OVER (ORDER BY q4_revenue DESC). When ORDER BY is present inside an aggregate window function, the default frame is RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW, which gives a running sum.
SUM(q4_revenue) OVER (ORDER BY q4_revenue DESC) AS running_total
5
Step 5 — Assemble the Final Query and Review OutputCombining all columns into a single query, we get a concise report that would otherwise require multiple subqueries or a temporary table. The final SQL is:
SELECT dept, q4_revenue, RANK() OVER (ORDER BY q4_revenue DESC) AS revenue_rank, ROUND(100.0 * q4_revenue / SUM(q4_revenue) OVER (), 1) AS pct_of_total, SUM(q4_revenue) OVER (ORDER BY q4_revenue DESC) AS running_total FROM dept_revenue ORDER BY revenue_rank;

Expected Output

Final output showing rank, percentage of total, and running total — all computed with window functions
deptq4_revenuerevenue_rankpct_of_totalrunning_total
Electronics520,000129.3520,000
Grocery480,000227.01,000,000
Apparel310,000317.51,310,000
Home Goods275,000415.51,585,000
Sports190,000510.71,775,000

Window Functions vs. GROUP BY — Strengths & Limitations

Window functions and GROUP BY are not competitors — they are complementary tools with different purposes. In many production queries they even appear together (you can apply a window function to already-grouped results). Understanding when to reach for each tool is a hallmark of an effective analyst.

Comparative analysis of GROUP BY vs. window function capabilities
DimensionGROUP BYWindow Function
Row outputOne summary row per groupAll original rows preserved
Detail + aggregateCannot display detail columns (unless included in GROUP BY)Detail and aggregate appear side by side in the same row
RankingNot directly supported; requires subqueries or variablesNative support via ROW_NUMBER, RANK, DENSE_RANK, NTILE
Running calculationsRequires correlated subqueries or application-level logicBuilt-in via ORDER BY in the OVER clause (running sum, running avg)
Period comparisonRequires self-join on offset dateLAG/LEAD access previous/next rows directly
PerformanceGenerally faster for pure summarization tasksMay require additional sorting; optimizers handle well at moderate scale
Filtering on resultHAVING clause filters groupsCannot use in WHERE; must wrap in CTE/subquery to filter
💡 WHEN TO USE WHICH
Use GROUP BY when your deliverable is a summary table — total revenue per quarter, average order value per customer segment. Use a window function when you need row-level detail enriched with contextual calculations — each employee's salary alongside their departmental rank, each transaction alongside a running balance, or each month's revenue alongside the prior month's figure for growth analysis. In practice, many analytical dashboards rely on both approaches in the same data pipeline.

Connection to Advanced Window Concepts

This introductory lesson establishes the foundational mechanics of window functions. As you move into advanced analytics coursework and real-world BI projects, several deeper topics build directly on these concepts. The table below maps what you have learned today to where each idea leads.

How introductory window function concepts connect to advanced analytical techniques
Intro Concept (This Lesson)Advanced ExtensionBusiness Application
PARTITION BY basicsMulti-column partitions and named window clauses (WINDOW w AS ...)Complex segmentation: rank products within subcategory within region
Default frame (RANGE UNBOUNDED PRECEDING)Custom ROWS/RANGE/GROUPS frames with BETWEEN7-day moving average of daily sales; trailing 12-month rolling revenue
RANK and ROW_NUMBERPERCENT_RANK, CUME_DIST, NTILE for statistical positioningCustomer lifetime-value percentiles; A/B/C/D quartile segmentation
LAG / LEAD basicsMulti-row offsets, default values for NULLs, nested offset logicYear-over-year growth rates, churn detection, sessionization of clickstream data
Window in SELECT clauseCTE + window function pipelines for multi-pass analyticsCohort analysis, funnel conversion rates, recursive financial models

One particularly powerful pattern in advanced work is combining Common Table Expressions (CTEs) with window functions. Because you cannot filter directly on a window function's result in the WHERE clause, analysts frequently write a CTE that computes the window function, and then an outer query that filters, pivots, or further aggregates those results. This two-pass approach powers many real-world analytics dashboards, from executive KPI reports to operational alerting systems.

🔭 Looking Ahead
In subsequent lessons, we will dive deep into frame specifications (ROWS vs. RANGE vs. GROUPS), explore the nuances among ROW_NUMBER, RANK, and DENSE_RANK with tie-handling examples, and build multi-step analytical pipelines that chain CTEs and window functions to solve complex business questions.

Practice Problems

Work through the following five problems to solidify your understanding of window function concepts. They progress from conceptual recall to critical analysis. Assume standard SQL syntax (compatible with PostgreSQL, SQL Server, or any SQL:2003-compliant engine).

PROBLEM 1CONCEPTUAL
Explain in your own words the fundamental difference between SUM(sales) ... GROUP BY region and SUM(sales) OVER (PARTITION BY region). Under what business circumstance would you prefer the window function version over the GROUP BY version?
PROBLEM 2BASIC CALCULATION
Given a table orders(order_id, customer_id, order_date, amount), write a SQL query that returns every order along with a column customer_total showing the total amount spent by that order's customer across all their orders.
PROBLEM 3INTERMEDIATE
Using the same orders table, write a query that numbers each customer's orders chronologically (earliest order = 1) and also shows the running total of their spending. Name the columns order_seq and running_spend. What is the default frame that makes the running total work?
PROBLEM 4APPLIED
A marketing manager asks: "Show me the top 3 products by revenue in each product category." You have a table product_sales(product_id, category, product_name, revenue). Write a complete query (including the necessary CTE or subquery) that returns only the top 3 products per category. Explain why you cannot use WHERE directly on the ranking column.
PROBLEM 5CRITICAL THINKING
Consider two queries on the orders table. Query A: SELECT customer_id, SUM(amount) OVER (PARTITION BY customer_id ORDER BY order_date ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW) AS running_total FROM orders; Query B: SELECT customer_id, SUM(amount) OVER (PARTITION BY customer_id ORDER BY order_date RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW) AS running_total FROM orders; Under what data condition would these two queries produce different results? Which version would a financial analyst typically prefer for a daily revenue running total, and why?

Lesson Summary

Window functions perform calculations across a defined set of rows — the window — without collapsing the result set. Every window function requires an OVER clause, which optionally accepts PARTITION BY (to divide rows into independent groups), ORDER BY (to define logical row ordering within each partition), and a frame specification (to fine-tune which rows relative to the current row participate in the calculation). Unlike GROUP BY, window functions preserve every row in the output, making them indispensable when you need detail-level data alongside aggregate context.

The three major categories are aggregate window functions (SUM, AVG, COUNT over a window for running totals and percentages), ranking functions (ROW_NUMBER, RANK, DENSE_RANK, NTILE for ordinal positioning), and value/offset functions (LAG, LEAD, FIRST_VALUE, LAST_VALUE for accessing other rows). Window functions execute during the SELECT phase of SQL's logical execution order — after WHERE, GROUP BY, and HAVING — which means filtering on their output requires a CTE or subquery wrapper. Mastering these concepts equips business analysts with a powerful, efficient toolkit for rankings, running totals, period-over-period comparisons, and percent-of-total calculations — all within a single SQL query.

Varsity Tutors • Business Analytics • Window Functions — Window function concepts (intro)