SQL Window Functions
SQL Window Functions
> P artition by
A window function makes a calculation across multiple rows that are related to We can use PARTITION BY together with OVER to specify the column over
the current row. For example, a window function allows you to calculate.
C omparing PARTITION BY with GROUP BY, we find the following similarity and
unning totals (i.e. sum values from all the rows before the current row
SQL Window Functions
R
difference
7-day moving averages (i.e. average values from 7 rows before the current row
Rankings
Just like GROUP BY, the OVER subclause splits the rows into as many partitions
Learn SQL online at [Link]
For example, using GROUP BY, we can calculate the average price of bicycles
per model year using the following query.
model_year,
AVG(list_price) avg_price
GROUP BY model_year
The product table contains the types of bicycles sold, their model year, and list What if we want to compare each product’s price with the average price of
price.
that year? To do that, we use the AVG() window function and PARTITION BY the
SELECT
1 Treak 820 - 2016 2016 379.99 Windows can be defined in the SELECT section of the query.
model_year,
product_name,
list_price,
windo w f
_ unction() OVER (
AVG(list_price) OVER
ORD ER BY order_e x
pressio n
avg_price
) AS window _column_alias
To reuse the same window with several window functions, define a named
The [order] table window using the WINDOW keyword. This appears in the query after the
HAVING section and before the ORDER BY section.
The order table contains the order_id and its date. SELECT
AV G A window frame is the selected set of rows in the partition over which
WIND W w
O w indo _name AS (
aggregation will occur. Put simply, they are a set of rows that are somehow
1 2016-01-01T00:00:00.000Z TITI N x n
D
PAR
OR ER
O
BY
BY
order_e
partition_e
x
pressio
pressio
w w f
indo _ rame_e xten t
2 2016-01-01T00:00:00.000Z )
A window frame is defined by a lower bound and an upper bound relative to
[ D
OR ER ...]
BY
the current row. The lowest possible bound is the first row, which is known as
3 2016-01-02T00:00:00.000Z
UNBOUNDED PRECEDING. The highest possible bound is the last row, which is
> Order by
4 2016-01-03T00:00:00.000Z
rows before the current row, then we will specify the range using 5 PRECEDING.
5 2016-01-03T00:00:00.000Z
PRECEDING
which the function assigns numbers to rows.
N PRECEDING
M FOLLOWING
Let's compare the following two queries which differ only in the ORDER BY clause.
LO G /* Rank price f
rom HI H-> W */
G LO FOLLOWING
1 20 0.2 SELECT
SELECT
product_name,
product_name,
1 8 0.07 list_price,
list_price,
RA NK () OVER
RA NK () OVER
1 10 0.05
> Accompanying Material
(OR DE R BY ist p ic
l _ r e SC
A ) rank
DE
(OR R BY ist p ic
l _ r e SC
A ) k
ran
1 16 0.05
FROM products
FROM products
1 4 0.2
You can use this [Link] to run any of the queries explained in
2 20 0.07
this cheat sheet.
> V alue window functions > LEAD, LAG
FIRST_VALUE() and LAST_VALUE() retrieve the first and last value respectively The LEAD and LAG locate a row relative to the current row.
SQL for Data Science from an ordered list of rows, where the order is defined by ORDER BY.
LEAD(expression
[,offset[,default_value]])
OVER(ORDER BY columns)
Learn SQL online at [Link] LAST_VALUE(value_to_return) OVER Returns the last value in an ordered set of LAG(expression Accesses the value stored in a row before
(ORDER BY value_to_order_by) values [,offset[,default_value]]) the current row.
OVER(ORDER BY columns)
NTH_VALUE(value_to_return, n) OVER Returns the nth value in an ordered set of
(ORDER BY value_to_order_by) values.
Both LEAD and LAG take three arguments
To compare the price of a particular bicycle model with the cheapest (or most Expression: the name of the column from which the value is retrieve
> Ranking window functions expensive) alternative, we can use the FIRST_VALUE (or LAST_VALUE).
/* Find the difference in price from /* Find the difference in price from
Offset: the number of rows to skip. Defaults to 1
Default_value: the value to be returned if the value retrieved is null.
the cheapest alternative */
the priciest alternative */
Defaults to NULL.
There are several window functions for assigning rankings to rows. Each of SELECT
SELECT
product_name,
product_name,
With LAG and LEAD, you must specify ORDER BY in the OVER clause.
FIRST_VALUE(list_price) OVER (
LAST_VALUE(list_price) OVER (
ORDER BY list_price
ORDER BY list_price
LEAD and LAG are most commonly used to find the value of a previous row or
The following are the ranking window functions and their description: ROWS BETWEEN
ROWS BETWEEN
UNBOUNDED PRECEDING
UNBOUNDED PRECEDING
the next row. For example, they are useful for calculating the year-on-year
AND
AND
increase of business metrics like revenue.
) AS cheapest_price,
) AS highest_price
FROM products
FROM products
Here is an example of using lag to compare this year's sales to last year's.
ROW_NUMBER()
Assigns a sequential integer Row numbers are not repeated within
to each row within the each partition.
WITH yearly_orders AS (
SELECT
year(order_date) AS year,
RANK()
Assigns a rank number to Tied values are given the same rank
COUNT(DISTINCT order_id) AS num_orders
GROUP BY year(order_date)
PERCENT_RANK() Assigns the rank number of Tied values are given the same rank
percentage.
Aggregate functions available for GROUP BY, such as COUNT(), MIN(), MAX(),
rank in the partition.
LAG(num_orders) OVER (ORDER BY year) - num_orders diff_from_last_year
with 100 rows, they will be in bucket Function Syntax Function Description
1, rows 21 to 40 in bucket 2, rows 41 ount the number of rows that have a non-
COUNT(expression) OVER (PARTITION C
to 60 in bucket 3, et cetera.
null expression in the partition.
BY partition_column)
partition.
partition_column)
partition.
WITH yearly_orders AS (
AVG(expression) OVER (PARTITION BY Find the mean (average) of the expression SELECT
We can use these functions to rank the product according to their prices. partition_column)
in the partition.
year(order_date) AS year,
SELECT
Suppose we want to find the average, maximum and minimum discount for GROUP BY year(order_date)
product_name,
)
list_price,
each product, we can achieve it as such.
FROM products
FROM order_items