Заимствование сильно от this approach, который также объясняется here более подробно вместе с некоторыми альтернативами, чтобы просто получить диапазоны дат, вы можете сделать:
with cte1 as
(
select begin_date as marker_date, 1 as type
from your_table
union all
select end_date + 1 as marker_date, -1 as type
from your_table
),
cte2 as (
select marker_date as begin_date,
lead(marker_date) over (order by marker_date) - 1 as end_date,
sum(type) over (order by marker_date) as periods
from cte1
)
select begin_date, end_date from cte2
where end_date is not null and periods > 0;
Что дает:
BEGIN_DATE END_DATE
---------- ----------
2015-01-01 2015-06-30
2015-07-01 2015-09-30
2015-10-01 2015-12-31
Я предположил, что вы на самом деле не хотите, чтобы сгенерированные периоды перекрывались на один день, и вместо этого хотите, чтобы они были началом и концом месяцев, такими как исходные две строки.
Чтобы получить суммы - если я понял, что вы описали - вы можете изменить, что включать изменение суммы на каждую дату, как положительные или отрицательные в зависимости от того, является ли это начало или конец периода:
with cte1 as
(
select begin_date as marker_date,
amount/months_between(end_date + 1, begin_date) as monthly_amount
from your_table
union all
select end_date + 1 as marker_date,
-amount/months_between(end_date + 1, begin_date) as monthly_amount
from your_table
),
cte2 as (
select marker_date as begin_date,
lead(marker_date) over (order by marker_date) - 1 as end_date,
sum(monthly_amount) over (order by marker_date) as total_monthly_amount
from cte1
)
select begin_date, end_date,
total_monthly_amount * months_between(end_date + 1, begin_date) as amount
from cte2
where end_date is not null and total_monthly_amount > 0;
BEGIN_DATE END_DATE AMOUNT
---------- ---------- ----------
2015-01-01 2015-06-30 6.66666667
2015-07-01 2015-09-30 6.83333333
2015-10-01 2015-12-31 3.5
Это работает путем деления суммы для исходного периода на количество месяцев, она охватывает:
select begin_date as marker_date, amount,
months_between(end_date + 1, begin_date) as months,
amount/months_between(end_date + 1, begin_date) as monthly_amount
from your_table
union all
select end_date + 1 as marker_date, amount,
months_between(end_date + 1, begin_date) as months,
-amount/months_between(end_date + 1, begin_date) as monthly_amount
from your_table;
MARKER_DATE AMOUNT MONTHS MONTHLY_AMOUNT
----------- ---------- ---------- --------------
2015-01-01 10 9 1.11111111
2015-07-01 7 6 1.16666667
2015-10-01 10 9 -1.11111111
2016-01-01 7 6 -1.16666667
и затем, используя это как КТР и применяя свинцовый аналитическую функцию для восстановления нового, не- периоды перекрытия:
with cte1 as
(
select begin_date as marker_date,
months_between(end_date + 1, begin_date) as months,
amount/months_between(end_date + 1, begin_date) as monthly_amount
from your_table
union all
select end_date + 1 as marker_date,
months_between(end_date + 1, begin_date) as months,
-amount/months_between(end_date + 1, begin_date) as monthly_amount
from your_table
)
select marker_date as begin_date,
lead(marker_date) over (order by marker_date) - 1 as end_date,
sum(monthly_amount) over (order by marker_date) as total_monthly_amount,
months_between(lead(marker_date) over (order by marker_date), marker_date) as months
from cte1;
BEGIN_DATE END_DATE TOTAL_MONTHLY_AMOUNT MONTHS
---------- ---------- -------------------- ----------
2015-01-01 2015-06-30 1.11111111 6
2015-07-01 2015-09-30 2.27777778 3
2015-10-01 2015-12-31 1.16666667 3
2016-01-01 0.00000000
И, наконец, за исключением искусственного открытый период в конце, плюс, что имеют нулевой итог в случае, если есть какие-либо пробелы (которые не имеют в небольшой выборке, но может появиться в больший набор данных); и умножить новую месячную сумму на количество месяцев в новом периоде.
Что такое правило для сумм - пропорционально #days? Что делать, если есть 4 записи и т. Д.? – OldProgrammer