2016-05-12 6 views
3

Ich habe eine Datenbank von IDs mit Einkommen und Anfangs-/Enddatum wie unten, aber ich habe Probleme, das Einkommen pro ID pro Monat für den angegebenen Anfangs-/Enddatumsbereich zu brechen.Wie schreibe ich eine Abfrage, um die Monate zwischen zwei Daten anzuzeigen?

Eine Probe der Tabellendaten unter:

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 

Das Ergebnis sollte:

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 

Wie würde ich die Oracle-SQL so schreiben, dass ich im Stande bin, das obige Ergebnis effizienter zu produzieren (Angenommen, die Tabelle hat Tausende von eindeutigen IDs)?

+0

Welche Version der Oracle-Datenbank? – mathguy

+0

Version 3.2.20.09 –

+0

? Aktuelle Oracle-Version ist 12, Sie sind sicherlich nicht auf Oracle 3? Um herauszufinden, Ihre Version, führen Sie 'Select * von v $ Version;' – mathguy

Antwort

2

Sie können dies verbinden mit durch, etwa so:

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

Danke, Boneist !! Sie arbeiten beide entsprechend! –

2

Sie recursive subquery factoring verwenden können, wenn Sie auf 11gR2 sind oder höher:

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 

Das Ankerelement erhält die Startinformationen - die ich zu Beginn des Monats, wahrscheinlich redundant, abschneide, aber nur für den Fall, dass man spät genug im Monat startet, um ein Problem mit dem Hinzufügen eines Intervalls zu verursachen. Das rekursive Element fügt dann jedem vorhandenen Element einen Monat hinzu, bis es das Enddatum erreicht.

+0

Danke, Alex !! Sie arbeiten beide entsprechend. –

+0

Wenn Sie viele Daten haben, prüfen Sie, welche Leistung besser ist; Ich vermute, Connected-by wird etwas schneller sein, obwohl es mit großen Datenmengen fehlschlagen kann. (Nun, so kann das, nehme ich an, irgendwann!) –

Verwandte Themen