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.
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;