Using the logic of new starting price window wins for overlaps.
Discreate Date version:
with data(article,price,startdate,enddate) as (
select * FROM VALUES
(123, 10, '2022-02-02'::date, '2049-12-31'::date),
(123, 8, '2022-02-14'::date, '2022-09-14'::date),
(123, 5, '2022-03-14'::date, '2022-04-06'::date),
(123, 4, '2022-04-11'::date, '2022-04-27'::date)
), dis_times as (
select article,
date as startdate,
lead(date) over(partition by article order by date)-1 as enddate
from (
select distinct article, startdate as date from data
union
select distinct article, enddate+1 as date from data
)
qualify enddate is not null
)
select
d1.article,
d1.price,
d2.startdate,
d2.enddate
from data as d1
join dis_times as d2
on d1.article = d2.article
and d2.startdate between d1.startdate and d1.enddate qualify row_number() over (partition by d1.article, s_startdate order by d1.startdate desc) = 1
order by 1,3;
gives:
| ARTICLE |
PRICE |
S_STARTDATE |
S_ENDDATE |
| 123 |
10 |
2022-02-02 |
2022-02-13 |
| 123 |
8 |
2022-02-14 |
2022-03-13 |
| 123 |
5 |
2022-03-14 |
2022-04-06 |
| 123 |
8 |
2022-04-07 |
2022-04-10 |
| 123 |
4 |
2022-04-11 |
2022-04-27 |
| 123 |
8 |
2022-04-28 |
2022-09-14 |
| 123 |
10 |
2022-09-15 |
2049-12-31 |
Continuous Timestamp version:
with data(article,price,startdate,enddate) as (
select * FROM VALUES
(123, 10, '2022-02-02'::date, '2049-12-31'::date),
(123, 8, '2022-02-14'::date, '2022-09-14'::date),
(123, 5, '2022-03-14'::date, '2022-04-06'::date),
(123, 4, '2022-04-11'::date, '2022-04-27'::date)
), dis_times as (
select article,
date as startdate,
lead(date) over(partition by article order by date) as enddate
from (
select distinct article, startdate as date from data
union
select distinct article, enddate as date from data
)
qualify enddate is not null
)
select
d1.article,
d1.price,
d2.startdate,
d2.enddate
from data as d1
join dis_times as d2
on d1.article = d2.article
and d2.startdate >= d1.startdate and d2.startdate < d1.enddate
qualify row_number() over (partition by d1.article, s_startdate order by d1.startdate desc) = 1
order by 1,3;
which gives:
| ARTICLE |
PRICE |
S_STARTDATE |
S_ENDDATE |
| 123 |
10 |
2022-02-02 |
2022-02-14 |
| 123 |
8 |
2022-02-14 |
2022-03-14 |
| 123 |
5 |
2022-03-14 |
2022-04-06 |
| 123 |
8 |
2022-04-06 |
2022-04-11 |
| 123 |
4 |
2022-04-11 |
2022-04-27 |
| 123 |
8 |
2022-04-27 |
2022-09-14 |
| 123 |
10 |
2022-09-14 |
2049-12-31 |
Thanks to MatBailie for the tighter join suggestion.
join dis_times as d2
on d1.article = d2.article
and d2.startdate between d1.startdate and d1.enddate
the continuous range I would normally do in this for
and d2.startdate between d1.startdate and d1.enddate and d2.startdate < d1.enddate
instead of this form
and d2.startdate >= d1.startdate and d2.startdate < d1.enddate
because I in experience it performed better. always test your complexities.