2016-12-23 213 views
2

我正在根據項目的預測預算查詢「未來幾個月」。通過返回比預期更多的行連接

基本上,我將最後一次預測日期(START_DATE)和我希望進行預測的未來日期(END_DATE),因此我需要在幾個月之間填入所有內容。

通過一些研究,我發現「CONNECT BY」可以幫助很多。

Simplyfying它,查詢看起來是這樣的:

SELECT  TO_CHAR (ADD_MONTHS (START_DATE, LEVEL - 1), 'fmMonth') 
    FROM (SELECT PROJECT_ID, FORECAST_VALUE, START_DATE, END_DATE 
      FROM PROJECTS 
      WHERE PROJECT_ID = 001) 
CONNECT BY LEVEL <= 
       MONTHS_BETWEEN (TRUNC (END_DATE, 'MM'), 
           TRUNC (START_DATE, 'MM') 
          ) 
      * +1 

選擇一個項目的時候,但是選擇幾個或全部項目/行時,查詢工作得很好,查詢分解,並返回許多比更多的行預期。

源數據是這個樣子:

PROJECT_ID | FORECAST_VALUE | START_DATE | END_DATE 
-----------+----------------+------------+----------- 
001  | 100   | 2017-01-01 | 2017-03-01 
002  | 200   | 2017-01-01 | 2017-05-01 
003  | 200   | 2017-10-01 | 2018-01-01 

我希望看到會是這樣的

PROJECT_ID | FORECAST_VALUE | FORECAST_YEAR | FORECAST_MONTH 
-----------+----------------+---------------+----------- 
001  | 100   | 2017   | JANUARY 
001  | 100   | 2017   | FEBRUARY 
001  | 100   | 2017   | MARCH 

002  | 200   | 2017   | JANUARY 
002  | 200   | 2017   | FEBRUARY 
002  | 200   | 2017   | MARCH 
002  | 200   | 2017   | APRIL 
002  | 200   | 2017   | MAY 

003  | 200   | 2017   | OCTOBER 
003  | 200   | 2017   | NOVEMBER 
003  | 200   | 2017   | DECEMBER 
003  | 200   | 2018   | JANUARY 

但是我得到一噸多的月和年超出預期。

我該如何解決這個問題?

謝謝!

+1

聽起來像是簡單的數據緻密化的問題。始終發佈源數據和期望結果的樣本。簡單的說。這裏是我有什麼,這裏是我所期望的輸出,這裏是我寫的查詢。 –

+0

@NicholasKrasnov謝謝!只是將其添加到我的問題。 – dkg

+0

和您正在運行的Oracle版本是什麼? –

回答

0

一個簡單的方法可以用數字表可以加入你的表,假設你將有不超過,說1000月:

select PROJECT_ID, FORECAST_VALUE, START_DATE, END_DATE, TO_CHAR (ADD_MONTHS (START_DATE, num - 1), 'fmMonth') 
from PROJECTS 
inner join (   
      select level as num 
      from dual 
      connect by level <= 1000 
      ) nums   
on (num -1 <= months_between(TRUNC (END_DATE, 'MM'), 
          TRUNC (START_DATE, 'MM')) ) 
order by 1, num 
1

既然你有比你把一個沒有其他條件在CONNECT BY中,每個級別的每行都會在下一級別生成更多的行(每個級別都沒有跟蹤每個PROJECT_ID)。您需要按PROJECT_ID = PRIOR PROJECT_ID鏈接行。但這會導致「週期」; CONNECT BY...通過僅查看受PRIOR運算符影響的列來檢測週期,而不是在所有列中。您可以通過添加不相關的PRIOR條件來打破循環,這將保證不同行的不同值;傳統上,SYS_GUID()用於此。

修改您的查詢,如下所示:

SELECT  TO_CHAR (ADD_MONTHS (START_DATE, LEVEL - 1), 'fmMonth') 
    FROM (SELECT PROJECT_ID, FORECAST_VALUE, START_DATE, END_DATE 
      FROM PROJECTS 
      WHERE PROJECT_ID = 001) 
CONNECT BY LEVEL <= 
       MONTHS_BETWEEN (TRUNC (END_DATE, 'MM'), 
           TRUNC (START_DATE, 'MM') 
          ) 
      * +1   -- whatever that means (copied from original post) 
     AND PROJECT_ID = PRIOR PROJECT_ID 
     AND PRIOR SYS_GUID() IS NOT NULL 

我假設,當然,PROJECT_ID是在基表PROJECTS唯一的密鑰(也許主鍵?)。

0

這是一種方法。我們只需取最小start_date和最大end_date並生成之間的所有內容,然後加入我們的projects表。

create table projects(project_id, forecast_value, start_date, end_date) as(
    select 001, 100, date '2017-01-01', date '2017-03-01' from dual union all 
    select 002, 200, date '2017-01-01', date '2017-05-01' from dual union all 
    select 003, 200, date '2017-10-01', date '2018-01-01' from dual 
); 


with 
    dates(dt) as(
     select add_months(s_date, level - 1) as dt 
     from (
      select min(start_date) as s_date 
       , max(end_date) as e_date 
       from projects 
      ) 
     connect by add_months(s_date , level - 1) <= e_date 
     ) 
select p.project_id 
    , p.forecast_value 
    , extract(year from d.dt) as forcast_year 
    , to_char(d.dt, 'MONTH') as forecast_month 
from projects p 
join dates d 
    on (trunc(d.dt, 'mm') between trunc(p.start_date, 'mm') 
          and trunc(p.end_date, 'mm')) 
order by p.project_id, d.dt 

結果:

PROJECT_ID FORECAST_VALUE FORCAST_YEAR FORECAST_MONTH 
---------- -------------- ------------ -------------- 
     1   100   2017 JANUARY  
     1   100   2017 FEBRUARY  
     1   100   2017 MARCH   
     2   200   2017 JANUARY  
     2   200   2017 FEBRUARY  
     2   200   2017 MARCH   
     2   200   2017 APRIL   
     2   200   2017 MAY   
     3   200   2017 OCTOBER  
     3   200   2017 NOVEMBER  
     3   200   2017 DECEMBER  
     3   200   2018 JANUARY  

12 rows selected. 
相關問題