'How to extract the oldest value for a column from a set of rows instead of all the rows?

I have a table in the following format

Name|Token|   Date  |
---------------------
John|7    |2010-4-30|
John|7    |2011-4-30|
John|9    |2011-5-30|
John|9    |2012-7-30|
John|9    |2015-1-30|
John|7    |2016-10-1|
John|9    |2016-11-3|
John|9    |2018-1-1 |
John|7    |2021-9-9 |

I am trying to copy data from this table to a new table and I need to find out the oldest date from the latest set of rows that have the token 9 for every person. Like in this case, I need to get 2016-11-3 as my output. Now for the cases where all token's are 9 I can easily use the group by clause and min(date) and get my result, but in cases like this if I use the group by clause and min(date) I would get results like john|9|2011-5-30| but it is not correct because the token 7 breaks the set on 2016-10-1.

Additional cases Case 1:

Name|Token|   Date  |
---------------------
John|7    |2010-4-30|
John|7    |2011-4-30|
John|9    |2011-5-30|
John|9    |2012-7-30|

Answer => 2011-5-30. because row no 3 and 4, both have token 9 and are right after each other hence the sequence

John|9    |2011-5-30|
John|9    |2012-7-30|

Case 2:

Name|Token|   Date  |
---------------------
John|7    |2010-4-30|
John|7    |2011-4-30|
John|9    |2011-5-30|
John|9    |2012-7-30|
John|9    |2015-1-30|
John|7    |2016-10-1|
John|9    |2016-11-3|
John|9    |2018-1-1 |
John|7    |2021-9-9 |
John|9    |2022-1-1 |

Answer -> 2022-1-1, because the latest set of rows which have 9 as token and are right after each other (in this case only 1 row) is

John|9    |2022-1-1 |

Case 3:

    Name|Token|   Date  |
    ---------------------
    John|9    |2010-4-30|
    John|9    |2011-4-30|
    John|9    |2011-5-30|
    John|9    |2012-7-30|
    John|9    |2015-1-30|
    John|9    |2016-10-1|
    John|9    |2016-11-3|
    John|9    |2018-1-1 |
    John|7    |2021-9-9 |

Answer ->2010-4-30, here the latest set of rows with token 9 which are in a sequence are

    John|9    |2010-4-30|
    John|9    |2011-4-30|
    John|9    |2011-5-30|
    John|9    |2012-7-30|
    John|9    |2015-1-30|
    John|9    |2016-10-1|
    John|9    |2016-11-3|
    John|9    |2018-1-1 |


Solution 1:[1]

This is a Gaps & Islands problem with a twist. You can do:

with
g as (
  select *,
    sum(inc) over(partition by name order by date) as grp
  from (
    select *,
      case when token <> lag(token) over(partition by name order by date) 
      then 1 else 0 end as inc
    from t
  ) x
)
select g.name, min(g.date) as min_date
from g
join (
  select name, max(grp) as max_grp from g where token = 9 group by name
) m on m.name = g.name and m.max_grp = g.grp
group by g.name

Result (with modified data for testing purposes):

 name   min_date   
 ------ ---------- 
 Alice  2019-03-01 
 John   2016-11-03 

See running example at db<>fiddle.

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 The Impaler