Skip to main content
April 2, 2026Dan Rodney/7 min read

Aggregate Window Functions in SQL

Master Advanced SQL Aggregation with Window Functions

Three Types of SQL Aggregate Functions

FeatureRegular AggregateGROUP BYWindow Functions
Usage FrequencyVery HighVery HighModerate
Data VisibilitySingle ResultGrouped ResultsOriginal + Aggregate
ComplexitySimpleModerateAdvanced
When to UseTotal calculationsCategory analysisDetailed comparisons
Recommended: Window functions are the least common but provide unique visibility into underlying data
Key Window Function Concept

Instead of replacing columns with aggregated results, window functions add new columns while preserving original data. You can see both what goes into the calculation and the result.

Window Functions vs Traditional Aggregates

Pros
Preserves original data while showing aggregated results
Enables detailed comparisons between individual and group values
Allows complex analysis within partitions
Provides transparency into calculation components
Cons
More complex syntax and concepts
Cannot be used directly in WHERE clauses
Less frequently needed than basic aggregates
Requires understanding of execution order

Basic Window Function Syntax

1

Choose Aggregate Function

Select your desired aggregate function like SUM, AVG, COUNT, etc.

2

Add OVER Clause

Include the OVER keyword to convert it to a window function

3

Define Window Scope

Use empty parentheses for entire dataset or PARTITION BY for groups

4

Alias the Result

Give your new column a meaningful name using AS

Employee Salary Analysis Example

Department 1 Average

Employees earning 100,000 and 110,000 result in 105,000 average. Window function shows both individual and average.

Department 2 Average

Salaries of 88,000 and 75,000 produce 81,500 average. Each employee sees their salary alongside department average.

Department 3 Single Employee

One person departments show individual salary as the average, demonstrating window function flexibility.

Window Functions Cannot Be Used in WHERE Clauses

Due to SQL execution order, window functions are created during SELECT phase, which happens after WHERE filtering. This requires subquery workarounds for filtering window function results.

SQL Execution Order Impact

1

FROM Clause

Database retrieves base data from specified tables

2

JOIN Operations

Any table joins are processed to combine data

3

WHERE Filtering

Rows are filtered based on conditions - window functions don't exist yet

4

SELECT Processing

Window functions are calculated and new columns are created

PARTITION BY is exactly like GROUP BY, but done as a windowed function. So you can see the underlying stuff, so you can see what went into the groups as well as your aggregate function.
Understanding the relationship between traditional GROUP BY and window function partitioning

GROUP BY vs PARTITION BY

FeatureGROUP BYPARTITION BY
Result StructureOne row per groupAll original rows retained
Data VisibilityOnly aggregated resultsIndividual + aggregated data
Column LimitationsMust include all SELECT columnsNo column restrictions
Use CaseSummary reportsDetailed analysis with context
Recommended: Use PARTITION BY when you need to see both individual records and their group aggregates
Subquery Workaround for WHERE Filtering

To filter window function results, wrap your window function query in a subquery and apply WHERE conditions in the outer query. This changes the execution order to make filtering possible.

Filtering Window Function Results

1

Create Inner Query

Write your window function query with all necessary columns and calculations

2

Wrap in Subquery

Place the window function query inside parentheses in the FROM clause

3

Add Required Alias

SQL requires subqueries in FROM to have an alias - use any name

4

Apply WHERE Filter

Use WHERE clause in outer query to filter on window function column results

Window Function Best Practices

0/5
Learning SQL's Unique Approach

SQL works in a very specific way with unique concepts and execution order. Understanding how it thinks makes complex operations more intuitive, even when they seem unintuitive at first.

This lesson is a preview from our SQL Course Online (includes software) and SQL Certification Online (includes software & exam). Enroll in a course for detailed lessons, live instructor support, and project-based training.

When I first introduced aggregate functions, I emphasized their fundamental importance in SQL. What makes them particularly powerful is their versatility—there are actually three distinct ways to leverage aggregate functions in your queries. We've already explored two methods, and now we'll dive into the third: window functions.

Before we proceed, let's establish realistic expectations. Window functions represent the least commonly used approach among the three. The bread-and-butter techniques—standard aggregate functions and GROUP BY operations—will comprise the vast majority of your day-to-day SQL work. This third method isn't intended to replace these foundational approaches; rather, it serves as a specialized tool for specific analytical scenarios.

While you'll reach for plain aggregates and GROUP BY operations regularly, window functions shine in particular circumstances where standard aggregation falls short. The key limitation of traditional aggregate functions and GROUP BY operations is their opacity—they compress multiple input values into a single output, effectively hiding the underlying data that contributed to the result.

Window functions solve this transparency problem elegantly. Think of them as providing a literal "window" into your data—you can see both the aggregated results and the individual records that comprise those calculations. This dual visibility becomes invaluable when you need to perform comparative analysis or maintain context while aggregating.

Consider the fundamental difference: traditional aggregate functions replace your original column with the computed result. You lose access to the granular data in exchange for the summary. Window functions take an additive approach—they preserve your original data while appending additional columns containing the aggregated calculations. This non-destructive methodology opens up analytical possibilities that simply aren't feasible with conventional aggregation.

Let's examine this concept through practical application. I'll connect to our standard company database and focus on the employees and departments tables that have served us well throughout this series.

Our employees table contains the essential information we need: employee names, email addresses, and salaries. For this demonstration, I'll streamline our view to show just the core elements—employee names, their department assignments, and salary information. This focused approach will help illustrate the concepts without unnecessary distractions.

When we examine this data, a traditional aggregate function would treat the entire salary column as a single unit. If we sum all salaries, we get the total payroll expense across the organization. While this information has value, notice what we've lost—the individual salary data that contributed to this total has been completely replaced by the aggregate result.

This replacement behavior is the defining characteristic of standard aggregate functions: multiple rows of input data collapse into a single row of output. The granular information that went into the calculation becomes inaccessible once the aggregation is complete.

Now, let's approach this differently using a window function. I want to maintain my three-column view—employee names, departments, and individual salaries—while adding aggregate information as a supplementary column.

If I attempt to simply add a SUM(salary) to my SELECT statement, the query fails with a familiar error. The database engine recognizes this as an aggregation that's incompatible with the non-aggregated columns in my selection. This is standard SQL behavior that we've encountered before.

The solution lies in a single, powerful keyword: OVER. By appending "OVER ()" to our aggregate function, we transform it into a window function. This tells the database to apply the aggregation across the entire dataset while preserving the underlying row structure.


The OVER clause is what distinguishes window functions from their traditional counterparts. It maintains our "window" into the underlying data while providing the aggregated insights we need. You'll notice that the total salary amount repeats for every row—this is intentional and represents the total payroll visible alongside each individual employee's contribution.

For clarity and maintainability, I recommend aliasing these calculated columns with descriptive names like "total_salary." Additionally, formatting longer queries across multiple lines significantly improves readability—a practice that becomes increasingly valuable as your queries grow in complexity.

While seeing the company-wide total alongside individual salaries has limited analytical value, changing our aggregation to AVG(salary) provides more meaningful insights. Now we can compare each employee's salary to the company average, immediately identifying outliers and patterns.

In our example, the CEO's salary significantly skews the company-wide average upward, making it less useful as a benchmark for most employees. This highlights a common analytical challenge—company-wide averages often mask important variations within different organizational segments.

The real power of window functions emerges when we introduce partitioning. Just as GROUP BY creates logical groups in traditional aggregation, the PARTITION BY clause segments our data for window function calculations. The terminology differs—"partition" instead of "group"—but the concept remains identical.

By adding "PARTITION BY department_id" to our OVER clause, we calculate department-specific averages while maintaining visibility into individual employee data. This approach provides the granular grouping benefits of GROUP BY while preserving access to the underlying records.

Looking at our results, Department One's employees earn $100,000 and $110,000, yielding a $105,000 average. Department Two shows $88,000 and $75,000 salaries with an $81,500 average. Single-employee departments display that individual's salary as both the personal and departmental average.

This capability—seeing both the forest and the trees—makes window functions particularly valuable for comparative analysis. You can immediately identify which employees fall above or below their departmental averages, enabling more nuanced workforce analysis.

However, window functions come with an important limitation: they cannot be used directly in WHERE clauses. Attempting to filter based on our calculated average_salary column results in an error stating "window functions are not allowed in WHERE."

This restriction stems from SQL's execution order. The WHERE clause processes before the SELECT clause creates our window function column. During the filtering phase, our calculated column simply doesn't exist yet—it won't be created until the SELECT phase completes.

Understanding this execution sequence is crucial for advanced SQL development. The database processes FROM (and any JOINs) first to establish the base dataset, then applies WHERE filtering, and only afterward executes the SELECT to create calculated columns like window functions.


Fortunately, we can work around this limitation using subqueries. By nesting our window function query within a parent query, we create two distinct execution contexts. The inner query completes its full execution cycle—including the SELECT phase that creates our window function column—before the outer query begins processing.

To implement this workaround, we wrap our existing query in parentheses and treat it as a table in the FROM clause of a parent query. SQL requires that subqueries in the FROM clause have aliases, so we'll assign an arbitrary name like "w" for "window."

Now our WHERE clause operates in the outer query context, where the average_salary column already exists as a regular column from the completed subquery. This enables filtering based on our window function calculations while respecting SQL's execution order constraints.

This subquery technique represents a common pattern in advanced SQL—using nested queries to manipulate execution order and access calculated values that wouldn't otherwise be available for filtering or further processing.

These concepts illustrate SQL's unique characteristics among programming languages. Unlike procedural languages where you might intuitively expect certain operations to work, SQL's declarative nature and specific execution order create constraints that require creative solutions.

For continued learning and reference, PostgreSQL's official documentation at PostgreSQL.org provides comprehensive coverage of all features, though it functions more as a technical reference than a learning resource. When researching specific problems, I recommend searching for "PostgreSQL" plus your specific question rather than generic "SQL" searches, which often return results for different database systems with varying syntax.

Stack Overflow and similar community resources often provide more practical, example-driven explanations than official documentation, particularly when you're trying to translate knowledge between different SQL dialects.

Your challenge exercise will revisit our Jeopardy dataset. Previously, we used correlated subqueries to find the highest point value questions within each category. Now, you'll solve this same problem using window functions instead of subqueries, demonstrating how different SQL techniques can achieve identical analytical goals.

This completes our exploration of the three aggregate function approaches: plain aggregates, GROUP BY operations, and window functions. While window functions represent the least frequently used method, they provide unique analytical capabilities that make them indispensable tools for complex data analysis scenarios. Master all three approaches, and you'll have the flexibility to choose the optimal technique for each analytical challenge you encounter.

Key Takeaways

1Window functions are the third and least common type of aggregate function in SQL, used when you need to see both original data and aggregated results simultaneously
2The OVER clause converts regular aggregate functions into window functions, preserving original rows while adding calculated columns
3PARTITION BY works like GROUP BY but maintains all original rows, allowing detailed analysis within groups while seeing individual data points
4Window functions cannot be used in WHERE clauses due to SQL execution order - WHERE executes before SELECT creates the window function columns
5Subqueries provide a workaround for filtering window function results by changing the execution order through nested query structure
6Window functions are particularly valuable for comparative analysis, such as showing individual salaries alongside department averages
7All subqueries in FROM clauses must have aliases in SQL, even when not explicitly used for joins or references
8While less frequently used than basic aggregates or GROUP BY, window functions provide unique analytical capabilities for detailed data exploration

RELATED ARTICLES