【问题标题】:Generating separate rows for each month in a date range为日期范围内的每个月生成单独的行
【发布时间】:2014-09-07 01:51:34
【问题描述】:

基本上我有一行数据,例如:

ID -   Start_Date -  End_Date

------------------------------------

XXA   1/23/14        3/12/14

我想在Start_DateEnd_Date 之间为每个月创建一行,对于每个ID,例如:

ID -  Month -     Year

--------------------------

XXA  January      2014

XXA  February     2014

XXA  March         2014

哪种方式最好、最有效?我正在考虑使用游标,但是一旦创建了这个表,我需要将其他表加入到这个表中。我是 oracle 新手,我不确定在运行游标并创建临时表后是否可以加入其他表。任何帮助将不胜感激。

【问题讨论】:

  • 首先创建一个简单的日历表并填充它。然后加入它。让我知道它是否足够清楚或需要更多帮助来完成。

标签: sql oracle plsql cursor reporting


【解决方案1】:

您可以使用CONNECT BY 语法来使用简单的行生成技术:

with sample_data as 
  (select 'XXA' id, to_date('1/23/14','MM/DD/RR') start_date, to_date('3/12/14','MM/DD/RR') end_date from dual)
select id, to_char(add_months(start_date,level - 1),'Month YYYY') date_column
from sample_data
connect by level <= extract(month from end_date) - extract(month from start_date) + 1;

编辑 我相信,添加DISTINCT 应该允许它跨行工作,尽管我有兴趣被证明是错误的。 编辑 2 修改示例以处理多年(最初应该这样做)。 (参见示例http://sqlfiddle.com/#!4/9eecb/4097/0。)

with sample_data as 
  ( select 'XXA' id, to_date('1/23/14','MM/DD/RR') start_date, to_date('3/12/15','MM/DD/RR') end_date from dual union all    
    select 'XXB' id, to_date('4/12/14','MM/DD/RR') start_date, to_date('6/18/15','MM/DD/RR') end_date from dual )
select distinct 
  id,
  to_char(add_months(start_date,level - 1),'Month YYYY') date_column,
  add_months(start_date,level -1) sortkey
from sample_data
connect by level <= ceil(months_between(trunc(end_date,'MM'), trunc(start_date,'MM'))) + 1
order by id, sortkey;

在我的沙盒数据库中,这会产生:

ID  DATE_COLUMN     SORT_COL
XXA January   2014  23-JAN-2014 00:00:00
XXA February  2014  23-FEB-2014 00:00:00
XXA March     2014  23-MAR-2014 00:00:00
XXB April     2014  12-APR-2014 00:00:00
XXB May       2014  12-MAY-2014 00:00:00
XXB June      2014  12-JUN-2014 00:00:00

【讨论】:

  • 嗨狼,感谢您的帮助。如果我只有一行,你的例子会很好用,但我需要一个表的解决方案,每行包含 1 个 ID 和一个日期范围。
  • ID - Start_Date - End_Date
  • XXA 1/23/14 3/12/14
  • XXB 4/12/14 6/18/14
  • @dorianpc 查看我的编辑。我认为通过将DISTINCT 添加到查询中,这仍然对您有用。
【解决方案2】:

要获得预期结果,需要一些年月数据。我建议两种可能的方式来提供它:

  1. 创建一个包含所有可能的年月的表或视图;
  2. 创建一个仅返回所需年月的表函数。

其他选项假设隐式生成年月数据,我认为这不是一个明显的直接解决方案。

第二个选项(理论上)在更大的输入数据上可能会更快,但我不推荐它,因为你对性能只字未提,而且它比第一个更复杂。

最简单易用的解决方案是:

create table year (id int primary key);
    insert into year values (2014);
    insert into year values (2015);
    insert into year values (2016);


create table month (id int primary key, name char(255));
    insert into month values (1, 'Jan');
    insert into month values (2, 'Feb');
    insert into month values (3, 'Mar');
    insert into month values (4, 'Apr');
    insert into month values (5, 'May');
    insert into month values (6, 'Jun');
    insert into month values (7, 'Jul');
    insert into month values (8, 'Aug');
    insert into month values (9, 'Sep');
    insert into month values (10, 'Oct');
    insert into month values (11, 'Nov');
    insert into month values (12, 'Dec');


create table data (id char(15) primary key , start_date date, end_date date);
    insert into data 
      values ('XXA', to_date('1/23/14','MM/DD/RR'), to_date('3/12/14','MM/DD/RR'));
    insert into data
      values ('XXB', to_date('4/12/14','MM/DD/RR'), to_date('6/18/14','MM/DD/RR'));


create or replace view calendar as
    select y.id*100 + m.id as id, y.id as year, m.name as month 
      from year y, month m;


select d.id, c.month, c.year 
    from  calendar c, data d
    where c.id between to_char(d.start_date, 'yyyymm') 
               and to_char(d.end_date, 'yyyymm');

输出是:

XXA|1月|2014年 ---+---+---- XXA|2月|2014年 ---+---+---- XXA|三月|2014 ---+---+---- XXB|2014 年 4 月 ---+---+---- XXB|五月|2014 ---+---+---- XXB|六月|2014

提前填写年表很重要,否则它会一直工作到 2016 年。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2017-03-22
    • 2018-12-06
    • 2023-01-12
    • 1970-01-01
    • 1970-01-01
    • 2013-07-07
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多