2015-06-03 1 views
0

У меня есть проблема, которую я не могу решить. У меня есть деньги A и B, которые я могу потратить в определенный период. Это следующие две строки в БД (с столбцами begin_date, end_date и amount):Сплит 2 строки строк в 3 по диапазонам дат

  • A: 2015.01.01.-2015.09.30. 10.000 $
  • B: 2015.07.01.-2015.12.31. 7.000 $

Таким образом, эти даты перекрываются, и это означает, что я могу потратить больше денег между 2017.07.01. и 2015.09.30. Поэтому на выходе я должен получить следующее:

  • 2015.01.01.-2015.07.01. x $
  • 2015.07.01.-2015.09.30. y $
  • 2015.09.30.-2015.12.31. z $

Как я могу выбрать эти диапазоны и подсчитать суммы, учитывая, что я трачу деньги одинаково за месяцы? Если я могу определить 3 диапазона дат, я думаю, что могу подсчитать суммы, но даты действительно сложны, и я не могу их обработать.

Я использую Oracle 11g.

+0

Что такое правило для сумм - пропорционально #days? Что делать, если есть 4 записи и т. Д.? – OldProgrammer

ответ

0

Заимствование сильно от 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   

И, наконец, за исключением искусственного открытый период в конце, плюс, что имеют нулевой итог в случае, если есть какие-либо пробелы (которые не имеют в небольшой выборке, но может появиться в больший набор данных); и умножить новую месячную сумму на количество месяцев в новом периоде.

+0

OMG, ты мой спаситель, это именно то, что мне нужно! – KatieAmber

Смежные вопросы