Search code examples
mysqlsqlwindow-functionsgaps-and-islandsanalytic-functions

How to get adjacent value in an OVER() window


I have the following data and query to get the season with the MAX(wins) up to the current season:

WITH results as (
    SELECT 'DAL' as team, 2010 as season, 6 as wins union
    SELECT 'DET' as team, 2010 as season, 6 as wins union
    SELECT 'DET' as team, 2011 as season, 10 as wins union
    SELECT 'DET' as team, 2012 as season, 4 as wins union
    SELECT 'DET' as team, 2013 as season, 7 as wins union
    SELECT 'DET' as team, 2014 as season, 11 as wins
) SELECT team, season, wins
    ,MAX(wins) OVER (PARTITION BY team ORDER BY season ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW) max_wins_thus_far
FROM results;

# team, season, wins, max_wins_thus_far
DAL, 2010, 6, 6
DET, 2010, 6, 6
DET, 2011, 10, 10
DET, 2012, 4, 10
DET, 2013, 7, 10
DET, 2014, 11, 11

Here we can see, for example, that for DET, 2011 has the max wins of 10, and so the "max_wins" column is 10 from 2011 until 2014, when it takes on the greater value of 11. However, I'd like to pull in the season with the highest win total up through that point. For example, here is how the results would look:

# team, season, wins, max_wins_thus_far, season_with_max_wins_thus_far
DAL, 2010, 6, 6, 2010
DET, 2010, 6, 6, 2010
DET, 2011, 10, 10, 2011 <-- 2011 has the most wins for DET
DET, 2012, 4, 10, 2011
DET, 2013, 7, 10, 2011
DET, 2014, 11, 11, 2014 <-- now 2014 is the season with the most wins...

How could this be done in an analytic function? The best I've been able to is to build an object with the data, but not sure where to go from there:

# team, season, wins, max_wins_thus_far
DAL, 2010, 6, {"2010": 6}
DET, 2010, 6, {"2010": 6}
DET, 2011, 10, {"2010": 6, "2011": 10}
DET, 2012, 4, {"2010": 6, "2011": 10, "2012": 4}
DET, 2013, 7, {"2010": 6, "2011": 10, "2012": 4, "2013": 7}
DET, 2014, 11, {"2010": 6, "2011": 10, "2012": 4, "2013": 7, "2014": 11}

Solution

  • You can use a second level of window functions. Just grab the most recent season where the wins is the maximum wins:

    SELECT r.*,
           MAX(CASE WHEN wins = max_wins_thus_far THEN season END) OVER (PARTITION BY team ORDER BY season) as max_season
    FROM (SELECT team, season, wins,
                 MAX(wins) OVER (PARTITION BY team ORDER BY season) as max_wins_thus_far
          FROM results
         ) r;
    

    Here is a db<>fiddle.