Aggregating Windows

All aggregate functions can be used as window functions instead. All you have to do is add a window with an OVER clause. This causes the aggregate function to behave differently than when you use it with GROUP BY.

Review

Let's review a query we saw when we learned about GROUP BY.

SELECT
  company,
  max(salary) AS top_salary
FROM employees
GROUP BY company;
company
VARCHAR
top_salary
INTEGER
Footbook
254000
Wamazon
277000
Poogle
300000

Remember that GROUP BY takes a column(s), looks at the values in each row for that column(s), and groups together the rows where that value is equal. This column(s), is pictured in green.

company salary , max() SELECT company max(salary) Poogle 293000 Wamazon 149000 company name salary Poogle Lindsay 293000 Poogle François 131000 Poogle Jenny 162000 Wamazon Remmy 149000 Wamazon Milton 42000 company GROUP BY company name salary Poogle (Lindsay, François, Jenny) (293000, 131000, 162000) Wamazon (Remmy, Milton) (149000, 42000)

The rest of the columns from that table, pictured in red, have their values globbed into collections. These cannot be selected directly. Aggregate functions are used here to convert these collections into single values, once per row. You are already acquainted with this usage.

New Usage

When aggregate functions are used with windows, there is no grouping happening on the table you are selecting from, i.e. the input table. Instead, the aggregate function aggregates the window. As with other window functions, it runs once per row and generates 1 value per row of the input table.

Here is an example.

SELECT
  max(salary) OVER (PARTITION BY company) AS company_max,
  *
FROM employees;
company_max
INTEGER
id
INTEGER
name
VARCHAR
company
VARCHAR
department
VARCHAR
job_title
VARCHAR
salary
INTEGER
start_date
DATE
254000
38
Ronald Graham
Footbook
Integration
Human Markets Orchestrator
254000
2014-08-13
254000
37
Gina DuBuque
Footbook
Integration
Dynamic Paradigm Liaison
95000
2017-02-23
254000
36
Whitney Goyette
Footbook
Integration
Corporate Program Analyst
176000
2016-12-10
254000
35
Laura Raynor
Footbook
Integration
Central Usability Technician
144000
2019-05-19
254000
34
Rex Treutel
Footbook
Integration
Human Division Representative
117000
2012-08-04
254000
33
Santiago Lockman
Footbook
Solutions
Direct Branding Developer
195000
2013-12-15
254000
32
Wilson Maggio
Footbook
Solutions
Principal Intranet Architect
254000
2011-05-12
254000
31
Frankie Glover
Footbook
Solutions
Dynamic Solutions Assistant
42000
2015-07-18
254000
30
Mrs. Carmen Blick
Footbook
Research
Customer Tactics Consultant
183000
2011-07-23
254000
29
Sean Wintheiser II
Footbook
Research
Legacy Configuration Director
226000
2017-08-24
254000
28
Toni Fahey
Footbook
Research
National Branding Director
22000
2017-12-06
254000
27
Mathew Harber
Footbook
Research
Investor Brand Associate
149000
2017-02-13
300000
13
Alberto Monahan
Poogle
Marketing
Central Response Director
162000
2017-02-10
300000
1
Claude Barton
Poogle
Functionality
Legacy Paradigm Executive
245000
2013-09-30
300000
2
Christy McLaughlin
Poogle
Functionality
Direct Web Coordinator
55000
2017-04-18
300000
3
Tami Glover
Poogle
Functionality
Principal Marketing Architect
262000
2017-10-12
300000
4
Ana Koepp
Poogle
Infrastructure
Global Factors Assistant
194000
2011-09-27
300000
5
Mae Leannon
Poogle
Infrastructure
Principal Usability Agent
139000
2018-01-08
300000
6
Norma Krajcik
Poogle
Infrastructure
Global Quality Analyst
214000
2010-01-16
300000
7
Cameron Jacobson
Poogle
Infrastructure
Internal Mobility Orchestrator
299000
2016-07-14
300000
8
Ruby Connelly
Poogle
Infrastructure
Corporate Division Liaison
136000
2013-08-19
300000
9
Antonio Treutel
Poogle
Infrastructure
Investor Quality Assistant
20000
2016-08-31
300000
10
Marianne Schamberger
Poogle
Marketing
Future Infrastructure Assistant
300000
2017-03-27
300000
11
Shirley Prohaska
Poogle
Marketing
Future Configuration Supervisor
105000
2010-10-11
300000
12
Jan Wiza
Poogle
Marketing
Investor Factors Engineer
293000
2018-09-20
277000
26
Dana Ernser
Wamazon
Quality
Chief Program Producer
93000
2012-10-22
277000
25
Ramon Rohan
Wamazon
Quality
International Applications Consultant
60000
2010-11-07
277000
24
Ollie Murphy
Wamazon
Quality
Dynamic Program Director
277000
2018-08-02
277000
23
Rosemary Hilll
Wamazon
Quality
Lead Markets Director
157000
2014-09-25
277000
22
Jessie Hodkiewicz
Wamazon
Solutions
Future Assurance Producer
167000
2019-03-24
277000
21
Sheri Labadie
Wamazon
Solutions
Dynamic Quality Planner
27000
2016-06-13
277000
20
Essie Legros
Wamazon
Solutions
Regional Metrics Analyst
243000
2011-02-20
277000
19
Candice Konopelski
Wamazon
Marketing
Human Usability Orchestrator
151000
2015-03-19
277000
18
Michele Larson
Wamazon
Marketing
Central Group Manager
151000
2016-07-07
277000
17
Eula Price
Wamazon
Marketing
Chief Identity Developer
186000
2018-11-09
277000
16
Billie Vandervort
Wamazon
Marketing
Senior Data Developer
118000
2010-01-03
277000
15
Erik Vandervort
Wamazon
Marketing
Forward Applications Analyst
136000
2017-02-14
277000
14
Alberto Dicki
Wamazon
Marketing
Central Division Technician
131000
2018-09-26

ORDER BY is not necessary in this window because max() is not sensitive to ordering. Actually, we have to omit it because it would change the frame, which we'll discuss a bit later.

The key difference here is that whereas GROUP BY aggregates individual employees into groups, aggregating on windows preserves the individual employee rows, and each row "sees" the result of max(salary).

company name salary Poogle Lindsay 293000 Poogle François 131000 Poogle Jenny 162000 Wamazon Remmy 149000 Wamazon Milton 42000 partition Wamazon Remmy 149000 Wamazon Milton 42000 partition Poogle Lindsay 293000 Poogle François 131000 Poogle Jenny 162000 company name salary max_salary Poogle Lindsay 293000 293000 Poogle François 131000 293000 Poogle Jenny 162000 293000 Wamazon Remmy 149000 149000 Wamazon Milton 42000 149000 *salary (…) , max() SELECT OVER company PARTITION BY

Every employee row can access the max salary of their respective company. This gives us yet another way to solve the problem of getting the top earner's name at each company.

WITH max_salaries AS (
  SELECT
    max(salary) OVER (PARTITION BY company) AS company_max,
    *
  FROM employees
)
SELECT *
FROM max_salaries
WHERE company_max = salary;
company_max
INTEGER
id
INTEGER
name
VARCHAR
company
VARCHAR
department
VARCHAR
job_title
VARCHAR
salary
INTEGER
start_date
DATE
254000
38
Ronald Graham
Footbook
Integration
Human Markets Orchestrator
254000
2014-08-13
254000
32
Wilson Maggio
Footbook
Solutions
Principal Intranet Architect
254000
2011-05-12
300000
10
Marianne Schamberger
Poogle
Marketing
Future Infrastructure Assistant
300000
2017-03-27
277000
24
Ollie Murphy
Wamazon
Quality
Dynamic Program Director
277000
2018-08-02

© 2022 Andrew Carlson. All rights reserved.