SQL Window Function — Demonstrated with Real Interview Questions from Leetcode | by Sherwin Zheng | Towards Data Science

By Sherwin Zheng
The window function is a must know in SQL interviews now, though it has just been available on, for example, MySQL 8.0 since its release on 19 April 2018.
Readers of this article are assumed to know SQL window functions other than basic SQL syntax. If you are not familiar with it and want to learn more, please refer to this [link] which I used for my learning. Please be noted that the syntax may differ on various SQL platforms.
This article will introduce you to several common applications of it that are tested during the interview using examples from Leetcode.com
The most recent Leetcode question 1596. The Most Frequently Ordered Products for Each Customer inspired me to write about this theme because it linked a bunch of knowledge points together, including the window function and its application from easy to intermediate, correlated subquery and its relationship with the window function, and the execution sequence of SQL statements.
First of all, let’s start with a simple question: what is the highest salary in each department? I think the answer pops up in your mind quickly: SELECT MAX(salary) with GROUP BY department .
However, what if the interviewer wants to crank it up, saying that the company wants to find out not only the highest salary but also the employee name corresponding to that salary. How do you write the query? In this sense, it is similar to the Level of Detail (LOD) operation in Tableau. This is Leetcode [184. Department Highest Salary]
There are two ways to tackle this problem: window function or correlated subquery, either one should work. You may find that they are equivalent while the window function is more concise and easier to write.
With the window function, easy-peasy, just write the code below
While with a correlated subquery, the code cost more time to run
For each row in the outer table, the correlated subquery will look up into the inner table in the WHERE clause, and compare the row in the outer table to those corresponding in the subquery. It is not limited to EXIST but can also give RANK() by using COUNT() with the WHERE statement.
Other good practice questions on Leetcode are:
[178. Rank Scores] (hard coding DESNE_RANK)
[177. Nth Highest Salary]
[185. Department Top Three Salaries]
My solutions to 178 are here. It may not be the best practice, but it is a good illustration showing how the mechanism works.
A brief note:
Correlated subquery + COUNT() = RANK(), which gives gaps when tie
Correlated subquery + COUNT(DISTINCT ) = DENSE_RANK(), which eliminate gaps when tie
Why is their equivalence worth mentioning? Well, if you are so lucky that your SQL platform does not support window function, you would have to engineer an equivalent one using a correlated subquery to achieve the same effect. So it is still good to know.
A sum-up of the above two examples:
We can find the least value in each group using GROUP BY + MIN(), but if we want to know its corresponding value under other fields, we need to use the window function to give a rank, equivalent to correlated subquery in the older SQL version or LOD in Tableau.
Let’s ramp it up again.
What if we want to know the rank of “annual salary”, but it is not yet here for us to ORDER BY in our window function before summing their “monthly payments” up?
For example, write a SQL query to find the most frequently ordered product(s) for each customer.
orders table+----------+------------+-------------+------------+| order_id | order_date | customer_id | product_id |+----------+------------+-------------+------------+| 1        | 2020-07-31 | 1           | 1          || 2        | 2020-07-30 | 2           | 2          || 3        | 2020-08-29 | 3           | 3          || 4        | 2020-07-29 | 4           | 1          || 5        | 2020-06-10 | 1           | 2          || 6        | 2020-08-01 | 2           | 1          || 7        | 2020-08-01 | 3           | 3          || 8        | 2020-08-03 | 1           | 2          || 9        | 2020-08-07 | 2           | 3          || 10       | 2020-07-15 | 1           | 2          |+----------+------------+-------------+------------+
Questions in the previous section are simple because the ranking field — salary — is presented. But in the table above, each row records a “product” purchased by a customer, with duplications.
If we adopt the previous reasoning, we need to (1) write a subquery: GROUP BY customer_id, product_id and COUNT(product_id) AS cnt, then(2) use cnt as the ranking field for the window function, and (3) filter the result in the WHERE clause, such as rnk = 1 in a third query because we cannot use window function in the WHERE statement.
However, we can actually combine t1 and t2 into one query as follows
In the beginning, it is not instinctive to me when combining PARTITION BY with GROUP BY: on which partition/group is the query based? But it unraveled after I reviewed the query order of execution:
FROM and JOIN s.WHERE ...GROUP BY ...HAVING ...SELECT ...DISTINCT ...ORDER BY ...LIMIT / OFFSET.
FROM and JOIN s.
WHERE ...
GROUP BY ...
HAVING ...
SELECT ...
DISTINCT ...
ORDER BY ...
LIMIT / OFFSET.
It is relevant to know that window functions are executed at the same stage as SELECT, so is everything coming with it, including PARTITION BY . Therefore, the actual execution step by step would be as below (Note: the code in the previous chunk won’t present CNT in the result, but it will calculate it as illustrated).
This is [Leetcode 1596. The Most Frequently Ordered Products for Each Customer]
A further thought by me was that what if we wanted to know the second most, or even the nth most-purchased product as we compare this question to [177. Nth Highest Salary]. How would you modify the code in the last chunk?
change rnk = 1 to rnk = 2, or
change rnk = 1 to rnk = 2 and change RANK() to DENSE_RANK() at the same time?
It is for you to think. After reading this article, you should be familiar with the relationship.
Mastering this type of data granularity question is helpful in many aspects. We started by introducing the value and usage of the window function and comparing it to simple GROUP BY. Then we gained deeper understandings about both window function and correlated subquery. Lastly, we learned how to rank any level of detail with data of higher granularity.
After this article, you should be able to handle this type of question whether in an interview or production and dig insights from any level of detail by practicing your SQL expertise with window functions.
About me
Sherwin is a Master of Science in Business Analytics at Wake Forest University, with an undergraduate major in Management Science at Sun Yat-sen University and a minor in Big Data from Rotterdam School of Management (RSM), Erasmus University. Check my LinkedIn or Github for more.