通过返回比预期更多的行来连接

时间:2016-12-23 21:37:43

标签: sql oracle

我正在查询根据项目的预测预算检索“未来月份”。

基本上我正在考虑最后一个预测日期(START_DATE)以及我希望将预测发布到(END_DATE)的未来日期,所以我需要填写几个月之间的所有内容。

通过一些研究,我发现“CONNECT BY”可以提供很多帮助。

简单地说,查询看起来像这样:

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

然而,我得到了比预期更多的月份和年份。

我该如何解决这个问题?

谢谢!

3 个答案:

答案 0 :(得分: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中的唯一键(可能是主键?)。

答案 1 :(得分: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  

答案 2 :(得分:0)

这是一种方法。我们只需使用最小export interface MyDate{ time: string; milliseconds_since_epoch: number; date: string; } export class AppComponent{ private data :MyDate = {}; // initialise the data here } getDatas() :Observable<MyDate>{ //return this._http.get('/app/test.json') return this._http.get('http://date.jsontest.com') .map(res => res.json()); } 和最大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