First we mark every time there’s a change in Value using lag and ordering by Date, and then we count to create distinct groups.
select min(date) as "From Date"
,max(date) as "To Date"
,type
,value
from (
select *
,count(chng) over(order by date) as grp
from (
select *
,case when value <> lag(value) over(order by date) then 1 end as chng
from t
) t
) t
group by grp, type, value
order by 1
This can be achieved by MATCH_RECOGNIZE, not sure if all keywords are supported in postgres but it works perfectly in oracle.
with tbl as (
select '2022-09-21' as "Date", 'S' as "Type", '10' as "Value" from dual union all
select '2022-09-22', 'S', '10' from dual union all
select '2022-09-23', 'S', '10' from dual union all
select '2022-09-24', 'S', '10' from dual union all
select '2022-09-25', 'S', '12' from dual union all
select '2022-09-26', 'S', '12' from dual union all
select '2022-09-27', 'S', '12' from dual union all
select '2022-09-28', 'S', '10' from dual union all
select '2022-09-29', 'S', '10' from dual union all
select '2022-09-30', 'S', '10' from dual)
select
*
from tbl
MATCH_RECOGNIZE (
order by "Date"
measures
first(chg1."Date") as "From Date",
First(chg2."Date") as "To Date",
chg1."Type" as "Type",
chg1."Value" as "Value"
one row per match
pattern(chg1 chg3+ chg2)
define
chg1 as "Value" <> PREV("Value") or (NVL(PREV("Value"),-1) = -1),
chg2 as "Value" <> NEXT("Value") or (NVL(NEXT("Value"),-1) = -1),
chg3 as NOT (("Value" <> PREV("Value") or (NVL(PREV("Value"),-1) = -1))
OR ("Value" <> NEXT("Value") or (NVL(NEXT("Value"),-1) = -1)))
);
2
Answers
First we mark every time there’s a change in
Value
usinglag
and ordering byDate
, and then wecount
to create distinct groups.Fiddle
This can be achieved by MATCH_RECOGNIZE, not sure if all keywords are supported in postgres but it works perfectly in oracle.