Many RDBMSs support the concept of named windows – windows that we can reference from within an OVER
clause when defining a window function.
For those of us using SQL Server, we had to wait until the release of SQL Server 2022 before we could create named windows for our window functions. But with the release of SQL Server 2022, we now have the option of using named windows in our window functions.
Named windows are made possible with the WINDOW
clause of the SELECT
statement. We use this clause to define the named window, and then we reference that window name in our SELECT
list for each column that needs to use the named window.
Database Compatibility
The WINDOW
clause requires database compatibility level 160 or higher. Here’s how to check your database’s compatibility level, and here’s how to change it (and here’s how to do both in SSMS).
Syntax
The syntax for the WINDOW
clause goes like this:
WINDOW window_name AS (
[ reference_window_name ]
[ <PARTITION BY clause> ]
[ <ORDER BY clause> ]
[ <ROW or RANGE clause> ]
)
<PARTITION BY clause> ::=
PARTITION BY value_expression , ... [ n ]
<ORDER BY clause> ::=
ORDER BY order_by_expression
[ COLLATE collation_name ]
[ ASC | DESC ]
[ ,...n ]
<ROW or RANGE clause> ::=
{ ROWS | RANGE } <window frame extent>
So we define it with WINDOW
, followed by the window name, followed by AS ()
. We insert the window’s definition in between the parentheses.
Example
Here’s a basic example to demonstrate:
SELECT
District,
Name,
Population AS "City Population",
SUM(Population) OVER win AS "District Population"
FROM City
WHERE CountryCode = 'AUS'
WINDOW win AS (PARTITION BY District);
Result:
District Name City Population District Population -------------------- --------------- --------------- ------------------- Capital Region Canberra 322723 322723 New South Wales Sydney 3276207 3993949 New South Wales Newcastle 270324 3993949 New South Wales Central Coast 227657 3993949 New South Wales Wollongong 219761 3993949 Queensland Brisbane 1291117 1805236 Queensland Gold Coast 311932 1805236 Queensland Townsville 109914 1805236 Queensland Cairns 92273 1805236 South Australia Adelaide 978100 978100 Tasmania Hobart 126118 126118 Victoria Geelong 125382 2990711 Victoria Melbourne 2865329 2990711 West Australia Perth 1096829 1096829
Here, I created a named window called win
. This named window has PARTITION BY District
as its definition. We refer to that named window in our OVER
clause, which causes the results to be partitioned by the District
column.
Modifying the Definition
We can also define other criteria in the OVER
clause when using named windows. This means we can define part of the window in the WINDOW
clause, and part of it in the OVER
clause.
This allows multiple columns to use the named window, but also have their own distinct criteria.
Here’s an example of what I mean:
SELECT
District,
Name,
Population,
SUM(Population) OVER win AS "Default",
SUM(Population) OVER(win ORDER BY Name ASC) AS "Ascending",
SUM(Population) OVER(win ORDER BY Name DESC) AS "Descending"
FROM City
WHERE CountryCode = 'AUS'
WINDOW win AS (PARTITION BY District);
Result:
District Name Population Default Ascending Descending -------------------- --------------- ----------- ----------- ----------- ----------- Capital Region Canberra 322723 322723 322723 322723 New South Wales Wollongong 219761 3993949 3993949 219761 New South Wales Sydney 3276207 3993949 3774188 3495968 New South Wales Newcastle 270324 3993949 497981 3766292 New South Wales Central Coast 227657 3993949 227657 3993949 Queensland Townsville 109914 1805236 1805236 109914 Queensland Gold Coast 311932 1805236 1695322 421846 Queensland Cairns 92273 1805236 1383390 514119 Queensland Brisbane 1291117 1805236 1291117 1805236 South Australia Adelaide 978100 978100 978100 978100 Tasmania Hobart 126118 126118 126118 126118 Victoria Melbourne 2865329 2990711 2990711 2865329 Victoria Geelong 125382 2990711 125382 2990711 West Australia Perth 1096829 1096829 1096829 1096829
In this case, three columns use the named window for its PARTITION BY
clause. But two of the columns have extra criteria, in the form of ORDER BY
clauses.
But it’s important to note that we can’t double up on clauses. We can only add clauses that aren’t in the named window. For example if the named window has a PARTITION BY
clause, we can’t add another PARTITION BY
clause to a column that refers to that named window. In other words, we can’t overwrite the definition in the WINDOW
clause, we can only add to it.
Here’s an example to demonstrate what I mean:
SELECT
District,
Name,
Population,
SUM(Population) OVER(win PARTITION BY District) AS "Population"
FROM City
WHERE CountryCode = 'AUS'
WINDOW win AS (PARTITION BY District);
Result:
Msg 4123, Level 15, State 2, Server bd79c358ea10, Line 69 Window element in OVER clause can not also be specified in WINDOW clause.
In this case I tried to use two PARTITION BY
clauses – one in the named window and one in the column that refers to that named window. This is not allowed and so I got an error.
Named Windows for More Concise Code
Named windows are a handy feature that can help us to make our code more concise. Named windows provide us with the opportunity to reuse code, so that we don’t need to replicate the same code multiple times.
Here’s an example to demonstrate:
SELECT
ProductPrice,
ROW_NUMBER() OVER win AS "Row Number",
RANK() OVER win AS "Rank",
DENSE_RANK() OVER win AS "Dense Rank"
FROM Products
WINDOW win AS (ORDER BY ProductPrice);
Result:
ProductPrice Row Number Rank Dense Rank ------------ -------------------- -------------------- -------------------- 9.99 1 1 1 10.00 2 2 2 11.99 3 3 3 12.45 4 4 4 14.75 5 5 5 25.99 6 6 6 25.99 7 6 6 33.49 8 8 7 55.99 9 9 8 245.00 10 10 9
Here, all three windows leverage the named window for its ORDER BY
clause.
While it’s true that we still have to replicate the window name (in this case win
), that still results in less code than what we would’ve had if we’d replicated its definition (i.e. ORDER BY ProductPrice
). This effect would be even greater if the definition included a PARTITION BY
clause and a ROW
or RANGE
clause.
Defining Multiple Named Windows
We can define multiple named windows with a single WINDOW
clause. To do this, we simply separate them with a comma:
SELECT
District,
Name,
Population,
SUM(Population) OVER win1 AS "Default",
SUM(Population) OVER win2 AS "Ascending",
SUM(Population) OVER win3 AS "Descending"
FROM City
WHERE CountryCode = 'AUS'
WINDOW
win1 AS (PARTITION BY District),
win2 AS (PARTITION BY District ORDER BY Name ASC),
win3 AS (PARTITION BY District ORDER BY Name DESC);
Result:
District Name Population Default Ascending Descending -------------------- --------------- ----------- ----------- ----------- ----------- Capital Region Canberra 322723 322723 322723 322723 New South Wales Wollongong 219761 3993949 3993949 219761 New South Wales Sydney 3276207 3993949 3774188 3495968 New South Wales Newcastle 270324 3993949 497981 3766292 New South Wales Central Coast 227657 3993949 227657 3993949 Queensland Townsville 109914 1805236 1805236 109914 Queensland Gold Coast 311932 1805236 1695322 421846 Queensland Cairns 92273 1805236 1383390 514119 Queensland Brisbane 1291117 1805236 1291117 1805236 South Australia Adelaide 978100 978100 978100 978100 Tasmania Hobart 126118 126118 126118 126118 Victoria Melbourne 2865329 2990711 2990711 2865329 Victoria Geelong 125382 2990711 125382 2990711 West Australia Perth 1096829 1096829 1096829 1096829
Window Chaining
Named windows can refer to other named windows in the same WINDOW
clause.
Example:
SELECT
District,
Name,
Population,
SUM(Population) OVER win1 AS "Default",
SUM(Population) OVER win2 AS "Ascending",
SUM(Population) OVER win3 AS "Descending"
FROM City
WHERE CountryCode = 'AUS'
WINDOW
win1 AS (PARTITION BY District),
win2 AS (win1 ORDER BY Name ASC),
win3 AS (win1 ORDER BY Name DESC);
Result:
District Name Population Default Ascending Descending -------------------- --------------- ----------- ----------- ----------- ----------- Capital Region Canberra 322723 322723 322723 322723 New South Wales Wollongong 219761 3993949 3993949 219761 New South Wales Sydney 3276207 3993949 3774188 3495968 New South Wales Newcastle 270324 3993949 497981 3766292 New South Wales Central Coast 227657 3993949 227657 3993949 Queensland Townsville 109914 1805236 1805236 109914 Queensland Gold Coast 311932 1805236 1695322 421846 Queensland Cairns 92273 1805236 1383390 514119 Queensland Brisbane 1291117 1805236 1291117 1805236 South Australia Adelaide 978100 978100 978100 978100 Tasmania Hobart 126118 126118 126118 126118 Victoria Melbourne 2865329 2990711 2990711 2865329 Victoria Geelong 125382 2990711 125382 2990711 West Australia Perth 1096829 1096829 1096829 1096829
In this example, both win2
and win3
refer to win1
, while also having their own individual ORDER BY
clauses.
However, a window component defined in one window cannot be redefined by another window referencing it.
Also, cyclic references and using multiple window references in a single window are not allowed.