2016-05-12 3 views
3

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

Образец данных таблицы ниже:

ID | INCOME | START_DATE | END_DATE 
1 | 2000 | 02/01/2016 | 05/31/2016 
1 | 1500 | 12/01/2015 | 01/31/2016 
2 | 1000 | 01/01/2016 | 04/30/2016 

Результат должен быть:

ID | INCOME | MONTH 
1 | 2000 | 05/2016 
1 | 2000 | 04/2016 
1 | 2000 | 03/2016 
1 | 2000 | 02/2016 
1 | 1500 | 01/2016 
1 | 1500 | 12/2015 
2 | 1000 | 04/2016 
2 | 1000 | 03/2016 
2 | 1000 | 02/2016 
2 | 1000 | 01/2016 

Как бы я писать Oracle SQL таким образом, что я в состоянии эффективно производить выше результата (если в таблице указаны тысячи уникальных идентификаторов)?

+0

Какая версия базы данных Oracle? – mathguy

+0

Версия 3.2.20.09 –

+0

? Текущая версия Oracle - 12, вы наверняка не используете Oracle 3? Чтобы узнать свою версию, запустите 'select * from v $ version;' – mathguy

ответ

2

Вы можете сделать это с помощью подключения по, например, так:

with sample_data as (select 1 id, 2000 income, to_date('01/02/2016', 'dd/mm/yyyy') start_date, to_date('31/05/2016', 'dd/mm/yyyy') end_date from dual union all 
        select 1 id, 1500 income, to_date('01/12/2015', 'dd/mm/yyyy') start_date, to_date('31/01/2016', 'dd/mm/yyyy') end_date from dual union all 
        select 2 id, 1000 income, to_date('01/01/2016', 'dd/mm/yyyy') start_date, to_date('30/04/2016', 'dd/mm/yyyy') end_date from dual) 
select id, 
     income, 
     add_months(trunc(start_date, 'mm'), -1 + level) mnth 
from sample_data 
connect by prior id = id 
      and prior income = income 
      and prior sys_guid() is not null 
      and add_months(trunc(start_date, 'mm'), -1 + level) <= trunc(end_date, 'mm') 
order by id, income desc, mnth desc; 


     ID  INCOME MNTH  
---------- ---------- --------- 
     1  2000 01-MAY-16 
     1  2000 01-APR-16 
     1  2000 01-MAR-16 
     1  2000 01-FEB-16 
     1  1500 01-JAN-16 
     1  1500 01-DEC-15 
     2  1000 01-APR-16 
     2  1000 01-MAR-16 
     2  1000 01-FEB-16 
     2  1000 01-JAN-16 
+0

Спасибо, Boneist !! Они оба работают соответственно! –

2

Вы можете использовать recursive subquery factoring, если вы на 11gR2 или выше:

with r (id, income, this_date, end_date) as (
    select id, income, trunc(start_date, 'MM'), trunc(end_date, 'MM') 
    from your_table 
    union all 
    select id, income, this_date + interval '1' month, end_date 
    from r 
    where end_date > this_date 
) 
select id, income, to_char(this_date, 'MM/YYYY') as month 
from r 
order by id, this_date desc; 

     ID  INCOME MONTH 
---------- ---------- ------- 
     1  2000 05/2016 
     1  2000 04/2016 
     1  2000 03/2016 
     1  2000 02/2016 
     1  1500 01/2016 
     1  1500 12/2015 
     2  1000 04/2016 
     2  1000 03/2016 
     2  1000 02/2016 
     2  1000 01/2016 

Член анкер получает стартовый информацию - который я усекаю до начала месяца, вероятно, излишне, но на случай, если в начале месяца достаточно поздно, чтобы вызвать проблему с добавлением интервалов. Затем рекурсивный член добавляет месяц к каждому существующему члену до тех пор, пока он не достигнет конечной даты.

+0

Спасибо, Алекс! Они оба работают соответственно. –

+0

Если у вас есть много данных, проверка которых выполняется лучше; Я подозреваю, что соединение будет немного быстрее, хотя оно может потерпеть неудачу с большими объемами данных. (Ну, может, это, наверное, в какой-то момент!) –

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