2016-12-23 218 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. 
相关问题