2017-03-15 56 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