Sql 通过返回比预期多的行进行连接

Sql 通过返回比预期多的行进行连接,sql,oracle,Sql,Oracle,我正在根据项目的预测预算查询未来月份 基本上,我将最后一个预测日期开始日期和未来日期(我希望将预测日期设置为结束日期),因此我需要用月份填充这两个日期之间的所有内容 通过一些研究,我发现“连接方式”可以帮助很多人 简单地说,查询如下所示: SELECT TO_CHAR (ADD_MONTHS (START_DATE, LEVEL - 1), 'fmMonth') FROM (SELECT PROJECT_ID, FORECAST_VALUE, START_DATE, END_D

我正在根据项目的预测预算查询未来月份

基本上,我将最后一个预测日期开始日期和未来日期(我希望将预测日期设置为结束日期),因此我需要用月份填充这两个日期之间的所有内容

通过一些研究,我发现“连接方式”可以帮助很多人

简单地说,查询如下所示:

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
然而,我得到了比预期多得多的月和年

我怎样才能解决这个问题


谢谢

一个简单的方法是将您的表与数字表连接起来,假设您的时间不超过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  

由于除了在“连接方式”中设置的条件外,没有其他条件,因此每一级别的每一行在下一级别生成更多的行,因此每一级别都没有对每个项目ID的跟踪。您需要按PROJECT\u ID=先前的PROJECT\u ID链接行,但这将导致循环;通过…连接。。。通过仅查看受先前运算符影响的列(而不是所有列)来检测周期。您可以通过添加一个不相关的先验条件来打破循环,该条件将保证不同行的不同值;传统上使用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是唯一的键,也许是主键?在基本表项目中。

这里有一种方法。我们只需取最小开始日期和最大结束日期,并生成其间的所有内容,然后加入到我们的项目表中


听起来像是简单的数据加密问题。始终发布源数据的样本和所需结果。简单地说。这是我所拥有的,这是我想要的结果,这是我写的问题。@NicholasKrasnov谢谢!刚刚添加到我的问题中。您运行的是什么版本的Oracle?@NicholasKrasnov 11g 11.2.0.4。0@NicholasKrasnov-也许这是一个简单的数据加密问题,但不是OP陈述的方式。在OP的描述中,每个项目的月份不同,仅取决于该项目的第一个月和最后一个月。在数据加密问题中,月份范围是可以同时容纳所有项目的最小范围。
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.