17th Oct 2024 8 minutes read The ORDER BY Clause in SQL Window Functions Tihomir Babic ORDER BY Window Functions Table of Contents What Are Window Functions? Syntax What Is the ORDER BY Clause in Window Functions? Example: ORDER BY with a Ranking Window Function ORDER BY and PARTITION BY in Window Functions Example: Using ORDER BY with PARTITION BY in a Window Function ORDER BY in Window Functions vs. Regular ORDER BY Example: ORDER BY in Window Functions and Regular ORDER BY Window Functions That Require ORDER BY Default Window Frames With and Without ORDER BY Using ORDER BY in Window Functions In this article, you’ll get an overview of ORDER BY in window functions. You’ll learn how it compares to PARTITION BY and the regular ORDER BY all accompanied by examples and explanations. If you want to create reports that go beyond simple aggregations, you’ll need SQL window functions. Window functions help you create rankings, compute running totals and moving averages, and find the difference between rows. To use window functions effectively, you must understand the role of the ORDER BY clause. Not only does it change the behavior of window functions, but some window functions won’t even run without ORDER BY. This article is for those who already have a general understanding of window functions. If you’re not familiar with them, I strongly recommend taking our Window Functions course. It will give you comprehensive knowledge of window functions, including ranking functions, analytics functions, and the ORDER BY and PARTITION BY clauses. The course features 218 coding challenges waiting to be solved; you can find more in the Window Functions Practice Set. Even if you know SQL window functions, you may want to keep our Window Functions Cheat Sheet close by for quick reference. What Are Window Functions? Window functions in SQL perform operations on a window frame, which consists of the current row and the rows related to it. Unlike aggregate functions, window functions don’t collapse individual rows; instead, they add a column to each row with the function result. This means window functions enable you to see individual and aggregate data simultaneously. Syntax The syntax of SQL window functions is shown below: window_function OVER ([PARTITION BY column_name] [ORDER BY column_name ASC|DESC]) Here’s a brief description of each part of the syntax: window_function: The window function you want to use. OVER(): A mandatory clause for creating a window function. PARTITION BY: An optional clause that partitions (divides) the data. ORDER BY: An optional clause that sorts the data within the window frame. What Is the ORDER BY Clause in Window Functions? ORDER BY (along with PARTITION BY) is a fundamental part of many window functions. ORDER BY in the window function sorts the rows within the window frame. It defines the order in which the window function computation will be performed. ORDER BY can sort data within a window ascendingly (A to Z, 1 to 10) or descendingly (Z to A, 10 to 1). You can sort text data in alphabetical or reverse alphabetical order, numerical data from lowest to highest (or vice versa), and date/time data from oldest to newest (or newest to oldest). Example: ORDER BY with a Ranking Window Function Let’s look at an example of how ORDER BY in window functions influences query execution. The query below ranks data in the table product_sales by sales in descending order – i.e., from the highest to the lowest sales. SELECT id, date, sales, product_name, DENSE_RANK() OVER(ORDER BY sales DESC) AS ranking FROM product_sales; DENSE_RANK() is one of the ranking window functions. It ranks the whole dataset from the highest to the lowest sales, which is specified in ORDER BY. The original product_sales table looks like this: iddatesalesproduct_name 12024-01-013,548.25Chorizo 22024-01-016,487.26Pierogi 32024-01-018,457.56Gyoza 42024-01-0212,567.44Pierogi 52024-01-021,478.69Chorizo 62024-01-022,489.15Gyoza 72024-01-035,479.99Gyoza 82024-01-038,845.54Chorizo 92024-01-039,748.23Pierogi Therefore, the code above will rank rows in the dataset descendingly: from highest to lowest. This is the query output, with the ranks shown in a separate column: iddatesalesproduct_nameranking 42024-01-0212,567.44Pierogi1 92024-01-039,748.23Pierogi2 82024-01-038,845.54Chorizo3 32024-01-018,457.56Gyoza4 22024-01-016,487.26Pierogi5 72024-01-035,479.99Gyoza6 12024-01-013,548.25Chorizo7 62024-01-022,489.15Gyoza8 52024-01-021,478.69Chorizo9 Now, the ranking would be significantly different if you replaced DESC with ASC in ORDER BY, as shown below: SELECT id, date, sales, product_name, DENSE_RANK() OVER(ORDER BY sales ASC) AS ranking FROM product_sales; Now the ranking is performed from the lowest to the highest sales: iddatesalesproduct_nameranking 52024-01-021,478.69Chorizo1 62024-01-022,489.15Gyoza2 12024-01-013,548.25Chorizo3 72024-01-035,479.99Gyoza4 22024-01-016,487.26Pierogi5 32024-01-018,457.56Gyoza6 82024-01-038,845.54Chorizo7 92024-01-039,748.23Pierogi8 42024-01-0212,567.44Pierogi9 ORDER BY and PARTITION BY in Window Functions We know what ORDER BY does in window functions. What about PARTITION BY? This is an optional clause that splits data into subsets based on one or more categories. We do this by specifying columns in the PARTITION BY clause like this: PARTITION BY product_name. I will demonstrate how this works in the following section. Without PARTITION BY, ORDER BY sorts the data in the whole result set. But use it with PARTITION BY and it will sort the data within each partition separately. Example: Using ORDER BY with PARTITION BY in a Window Function When used with PARTITION BY, ORDER BY sorts the data within each partition. Using ORDER BY with the SUM() window function produces a cumulative sum (i.e. the sum of the current row value plus all the rows before it in the partition). For example, the code below calculates the cumulative sum of sales by product name (as specified in PARTITION BY) from the oldest to the latest date (as specified in ORDER BY). SELECT id, date, sales, product_name, SUM(sales) OVER(PARTITION BY product_name ORDER BY date ASC) AS cumulative_sum FROM product_sales; The query will first split the dataset into partitions by product name. Then the values within each partition will be sorted from the oldest to the newest date, which is specified in ORDER BY. Then the SUM() window function will calculate the cumulative total by summing the current date’s sales with the sales of all the previous sales within the partition. Here’s the output, which shows each row and the cumulative sales for each product: iddatesalesproduct_namecumulative_sum 12024-01-013,548.25Chorizo3,548.25 52024-01-021,478.69Chorizo5,026.94 82024-01-038,845.54Chorizo13,872.48 32024-01-018,457.56Gyoza8,457.56 62024-01-022,489.15Gyoza10,946.71 72024-01-035,479.99Gyoza16,426.70 22024-01-016,487.26Pierogi6,487.26 42024-01-0212,567.44Pierogi19,054.70 92024-01-039,748.23Pierogi28,802.93 If we remove ORDER BY from the code, as shown below … SELECT id, date, sales, product_name, SUM(sales) OVER(PARTITION BY product_name) AS cumulative_sum FROM product_sales; … the code will return the total sales sum for each product. In other words, by simply omitting ORDER BY from a window function, you lose the ability to calculate cumulatively. Without ORDER BY, all rows in the partition make the window frame. iddatesalesproduct_namecumulative_sum 12024-01-013,548.25Chorizo13,872.48 52024-01-021,478.69Chorizo13,872.48 82024-01-038,845.54Chorizo13,872.48 62024-01-022,489.15Gyoza16,426.70 72024-01-035,479.99Gyoza16,426.70 32024-01-018,457.56Gyoza16,426.70 42024-01-0212,567.44Pierogi28,802.93 92024-01-039,748.23Pierogi28,802.93 22024-01-016,487.26Pierogi28,802.93 ORDER BY in Window Functions vs. Regular ORDER BY When I say regular, I mean the standard ORDER BY at the end of the query. How is it different from ORDER BY in a window function? A regular ORDER BY sorts a query output, while ORDER BY in window functions sorts a data window or a data partition. Example: ORDER BY in Window Functions and Regular ORDER BY This query uses the DENSE_RANK() window function with PARTITION BY and ORDER BY to rank sales dates for each product: SELECT id, date, sales, product_name, DENSE_RANK() OVER(PARTITION BY product_name ORDER BY sales DESC) AS day_rank FROM product_sales; You can see that the overall output is not sorted from the highest to the lowest sales. The descending sorting of the dates is applied only within each partition. iddatesalesproduct_namesales_rank_by_product 82024-01-038,845.54Chorizo1 12024-01-013,548.25Chorizo2 52024-01-021,478.69Chorizo3 32024-01-018,457.56Gyoza1 72024-01-035,479.99Gyoza2 62024-01-022,489.15Gyoza3 42024-01-0212,567.44Pierogi1 92024-01-039,748.23Pierogi2 22024-01-016,487.26Pierogi3 If you want your output to be sorted descendingly by sales, you have to do that explicitly by adding ORDER BY at the end of the query: SELECT id, date, sales, product_name, DENSE_RANK() OVER(PARTITION BY product_name ORDER BY sales DESC) AS sales_rank_by_product FROM product_sales ORDER BY sales DESC; Now you get the output sorted by sales and date. You can see how a date’s sales rank compares to other dates’ sales of the same product. iddatesalesproduct_namesales_rank_by_product 42024-01-0212,567.44Pierogi1 92024-01-039,748.23Pierogi2 82024-01-038,845.54Chorizo1 32024-01-018,457.56Gyoza1 22024-01-016,487.26Pierogi3 72024-01-035,479.99Gyoza2 12024-01-013,548.25Chorizo2 62024-01-022,489.15Gyoza3 52024-01-021,478.69Chorizo3 Window Functions That Require ORDER BY As you saw in the previous example, window functions can work without ORDER BY. ORDER BY is generally considered an optional clause in window functions. But even though ORDER BY in window functions is viewed as an optional clause, some window functions require ORDER BY to work at all. In those cases, ORDER BY becomes mandatory; those window functions require a sort order to work correctly. These window functions are: RANK() DENSE_RANK() NTILE() LEAD() LAG() Default Window Frames With and Without ORDER BY A window function’s behavior does change depending on whether it’s written with or without ORDER BY. More precisely, the presence or absence of ORDER BY impacts the default window frame. If there’s no ORDER BY, the default window frame includes the current row and all the rows preceding and following it. In other words, all rows in the partition are included. We have seen this with the cumulative sum example: without ORDER BY, the window frame defaults to the whole partition and the sum becomes the total sum. If there is ORDER BY, the window frame includes the current and all the preceding values. We have seen this with the cumulative sum example: with ORDER BY, the window frame includes all rows preceding the current row and the current row. If you don’t like the default window frame, you can define it explicitly using the ROWS and RANGE keywords. Using ORDER BY in Window Functions There you have it: ORDER BY – sometimes mandatory, sometimes optional – is the clause that makes and occasionally breaks window functions. However, knowing all about ORDER BY in window functions isn’t worth anything if you can’t use it in your queries. So, test what you learned here by solving the coding challenges in our Window Function course and Window Functions Practice Set. For even more exercises, solve these 11 window function exercises and answer the top 10 window function interview questions. Happy learning! Tags: ORDER BY Window Functions