2017-03-15 123 views
0

我有一个每个月计划的计划表。而这张桌子在那个月内还有休息日。我需要一个结果集来说明那个月的工作日和休息日。例如,Oracle - 将记录拆分为多个记录

CREATE TABLE SCHEDULE(sch_yyyymm varchar2(6), sch varchar2(20), sch_start_date date, sch_end_date date); 

INSERT INTO SCHEDULE VALUES('201703','Working Days', to_date('03/01/2017','mm/dd/yyyy'), to_date('03/31/2017','mm/dd/yyyy')); 

INSERT INTO SCHEDULE VALUES('201703','Off Day', to_date('03/05/2017','mm/dd/yyyy'), to_date('03/07/2017','mm/dd/yyyy')); 

INSERT INTO SCHEDULE VALUES('201703','off Days', to_date('03/08/2017','mm/dd/yyyy'), to_date('03/10/2017','mm/dd/yyyy')); 

INSERT INTO SCHEDULE VALUES('201703','off Days', to_date('03/15/2017','mm/dd/yyyy'), to_date('03/15/2017','mm/dd/yyyy')); 

使用SQL或PL/SQL我需要将记录与工作日和休息日分开。

201703 Working Days 03/01/2017 - 03/04/2017 
201703 Off Days  03/05/2017 - 03/10/2017 
201703 Working Days 03/11/2017 - 03/14/2017 
201703 Off Days  03/15/2017 - 03/15/2017 
201703 Working Days 03/16/2017 - 03/31/2017 

感谢您的帮助: 从上面记录我需要的结果集。

+0

所以你想选择整个表?两列之间只有一个破折号?作为一个? – 3kings

回答

0

编辑:我有更多的是想起了一下,这种方法工作得很好你插入记录上面 - 但是,它忽略了那里并没有连续的“休息日”期间的记录。我需要更多的思考,然后将做出一些改变

我已经放在一起测试使用leadlag函数和自联接。

结果是您自己将“休息日”加入到现有表中以查找重叠部分。然后计算每条记录任一侧的开始/结束日期。有一点逻辑可以让我们计算出哪个日期作为最终的开始/结束日期。

SQL小提琴here - 我使用Postgres作为Oracle功能不起作用,但它应该可以翻译好。

select sch, 
     /* Work out which date to use as this record's Start date */ 
     case when prev_end_date is null then sch_start_date 
      else off_end_date + 1 
     end as final_start_date, 
     /* Work out which date to use as this record's end date */ 
     case when next_start_date is null then sch_end_date 
      when next_start_date is not null and prev_end_date is not null then next_start_date - 1 
      else off_start_date - 1 
     end as final_end_date 
from (
select a.*, 
     b.*, 
     /* Get the start/end dates for the records on either side of each working day record */ 
     lead(b.off_start_date) over(partition by a.sch_start_date order by b.off_start_date) as next_start_date, 
     lag(b.off_end_date) over(partition by a.sch_start_date order by b.off_start_date) as prev_end_date 
from (
      /* Get all schedule records */ 
      select sch, 
       sch_start_date, 
       sch_end_date 
      from schedule 
     ) as a 
     left join 
     (
      /* Get all non-working day schedule records */ 
      select sch as off_sch, 
       sch_start_date as off_start_date, 
       sch_end_date as off_end_date 
      from schedule 
      where sch <> 'Working Days' 
     ) as b 
     /* Join on "Off Days" that overlap "Working Days" */ 
     on a.sch_start_date <= b.off_end_date 
     and a.sch_end_date >= b.off_start_date 
     and a.sch <> b.off_sch 
) as c 
order by final_start_date 
+0

感谢您的回复。我可以从这里拿走它并使其工作。将回答标记为正确。谢谢 – niceApp

+0

很高兴帮助!我仍然渴望得到它的兴趣,所以我会不停地修补。我遇到的问题是,自加入需要在每个工作日期间生成n + 1条记录,其中n是重叠的关闭日期的数量。上面的代码目前不可靠 – Alex

0

如果你有一个日期表,这将会更容易。

您可以使用递归cte和join来构造一个日期表。然后使用行号方法的差异将连续日期中具有相同时间表的行分为一组,然后获得每个组的minmax,这些组将是给定sch的开始日期和结束日期。 我假设只有2个sch值Working DaysOff Day

with dates(dt) as (select date '2017-03-01' from dual 
        union all 
        select dt+1 from dates where dt < date '2017-03-31') 
,groups as (select sch_yyyymm,dt,sch, 
      row_number() over(partition by sch_yyyymm order by dt) 
      - row_number() over(partition by sch_yyyymm,sch order by dt) as grp  
      from (select s.sch_yyyymm,d.dt, 
        /*This condition is to avoid a given date with 2 sch values, as 03-01-2017 - 03-31-2017 are working days 
        on one row and there is an Off Day status for some of these days. 
        In such cases Off Day would be picked up as sch*/ 
        case when count(*) over(partition by d.dt) > 1 then min(s.sch) over(partition by d.dt) else s.sch end as sch 
        from dates d 
        join schedule s on d.dt >= s.sch_start_date and d.dt <= s.sch_end_date 
       ) t 
      )  
select sch_yyyymm,sch,min(dt) as start_date,max(dt) as end_date 
from groups 
group by sch_yyyymm,sch,grp 

我无法获得在Oracle中运行的递归cte。这里是一个使用SQL Server的演示。

Sample Demo in SQL Server