sqlsql-servergaps-and-islands

Gaps and Islands Getting Last Island Window


I have a table with status changes that I want to chunk into islands and eventually get my most current island based on when my status changed. I'm using this query to retrieve the current, previous and next status codes from my table:

select 
    *, 
    case 
        when ping.GpsStatusTypeId <> ping.previousStatus 
            then 1 
            else 0 
    end as islandStarted
from
    (select 
         Id,
         GpsStatusTypeId,
         CreatedAt,
         lag(GpsStatusTypeId, 1) over (order by CreatedAt) as previousStatus,
         lead(GpsStatusTypeId, 1) over (order by CreatedAt) as nextStatus
     from 
         dbo.GpsPing p) ping

The result looks like this:

Id GpsStatusTypeId CreatedAt previousStatus nextStatus islandStarted
78E8C372-B7BE-4EED-8600-4925E7B66DBF 6 2025-04-23 20:31:10.917 6 21 0
5CB42B3F-2542-4372-A169-5B664D971152 21 2025-04-23 17:46:40.217 6 21 1
F57421EF-43AE-42C5-B766-C1CC07277B2E 21 2025-04-24 15:50:38.000 21 21 0
3C07F71E-39EF-4728-B0EF-DF8E5B9AE529 21 2025-04-24 17:07:38.000 21 21 0
5CB42B3F-2542-4372-A169-5B664D971152 21 2025-04-24 17:08:38.000 21 NULL 0

I'm looking to pull out only my last island (GpsStatusTypeId = 21) from the result.


Solution

  • You need a running COUNT to count up how many islands you passed. This becomes the group ID. Then just filter for the first group ID.

    In this case it's probably better to go descending, and flip the case to =

    with WithLag as (
        select
          Id,
          GpsStatusTypeId,
          CreatedAt,
          lag(GpsStatusTypeId) over (order by CreatedAt DESC) as previousStatusDesc
        from dbo.GpsPing p
    ),
    WithCount as (
        select *,
          case when ping.GpsStatusTypeId = ping.previousStatus then null else 1 end as islandStarted,
          count(case when ping.GpsStatusTypeId = ping.previousStatus then null else 1 end)
               over (order by CreatedAt DESC) as groupId
        from WithLag
    )
    select *
    from WithCount
    where groupId = 0;