2017-01-30 12 views
0

勤務時間に基づいてタスクの開始から終了までの時間を計算します。 I次のサンプルデータを持っている:Oracle SQLの勤務時間に基づいて時間を計算します

TASK | START_TIME | END_TIME 
A | 16-JAN-17 10:00 | 23-JAN-17 11:35 
B | 18-JAN-17 17:53 | 19-JAN-17 08:00 
C | 13-JAN-17 13:00 | 17-JAN-17 14:52 
D | 21-JAN-17 10:00 | 30-JAN-17 08:52 

をし、私は2つの違いをうまくする必要がありますが、次の営業時間に基づいて:

Mon - Sat 08:00 - 18:00 

私は確信して計算を作成する方法を知っているがありません計算に営業時間を追加するにはどうすればいいですか。

アドバイスをいただければ幸いです。

+1

週末や祝日はどうですか? –

+1

お返事ありがとうございました。土曜日は営業時間(08:00 - 18:00)としてカウントされ、日曜日は除外され、祝日は忘れてしまいますので、今年はそれを考慮する必要があります。 – user3191160

+0

夏時間はどのように変更されますか? –

答えて

0

は、各作業日の1行を生成し、それぞれの日のための時間を合計する相関階層問合せを使用します。

SELECT task, 
     COALESCE(SUM(end_time - start_time), 0) * 24 AS total_hours 
FROM (
    SELECT task, 
     GREATEST(t.start_time, d.column_value + INTERVAL '8' HOUR) AS start_time, 
     LEAST(t.end_time, d.column_value + INTERVAL '18' HOUR) AS end_time 
    FROM your_table t 
     LEFT OUTER JOIN 
     TABLE(
      CAST(
      MULTISET(
       SELECT TRUNC(t.start_time + LEVEL - 1) 
       FROM DUAL 
       WHERE TRUNC(t.start_time + LEVEL - 1) - TRUNC(t.start_time + LEVEL - 1, 'iw') < 6 
       CONNECT BY TRUNC(t.start_time + LEVEL - 1) < t.end_time 
      ) AS SYS.ODCIDATELIST 
      ) 
     ) d 
     ON ( t.end_time > d.column_value + INTERVAL '8' HOUR 
      AND t.start_time < d.column_value + INTERVAL '18' HOUR) 
) 
GROUP BY task; 
+0

MT0、謝罪、私の理解のためだけに説明したり、コードにコメントを追加することができます。希望はOKです。 – user3191160

+0

'DUALからLEVELをレベル<10 'で接続すると10行が生成されます - 関連する階層的なクエリは、' start_time'から 'end_time'までの1日増分の日のリストを生成するだけです。あなたのテーブル。その階層クエリの 'WHERE'節は、日曜日を非稼働日として削除します。 – MT0

+0

'GREATEST'と' LEAST'は、範囲内の各日の開始時刻と終了時刻を見つけ出し、最も外側のクエリでは、タスクごとに開始時刻と終了時刻の差が合計されます。 – MT0

0

この問題の私のお気に入りは、ビルドインSCHEDULER SCHEDULEを使用することです。

あなたは、必要に応じて、祝日のような例外のいくつかのスケジュールを作成し、DBMS_SCHEDULER.EVALUATE_CALENDAR_STRING

ファーストを使用して関数を作成する必要があります。米国の銀行日間 ここでは一例:ドイツ銀行日間

BEGIN 
    DBMS_SCHEDULER.CREATE_SCHEDULE('NEW_YEARS_DAY', repeat_interval => 'FREQ=YEARLY;INTERVAL=1;BYDATE=0101'); 
    DBMS_SCHEDULER.CREATE_SCHEDULE('MARTIN_LUTHER_KING_DAY', repeat_interval => 'FREQ=MONTHLY;BYMONTH=JAN;BYDAY=3 MON', comments => 'Third Monday of January'); 
    DBMS_SCHEDULER.CREATE_SCHEDULE('WASHINGTONS_BIRTHDAY', repeat_interval => 'FREQ=MONTHLY;BYMONTH=FEB;BYDAY=3 MON', comments => 'Third Monday of February'); 
    DBMS_SCHEDULER.CREATE_SCHEDULE('MEMORIAL_DAY', repeat_interval => 'FREQ=MONTHLY;BYMONTH=MAY;BYDAY=-1 MON', comments => 'Last Monday of May'); 
    DBMS_SCHEDULER.CREATE_SCHEDULE('INDEPENDENCE_DAY', repeat_interval => 'FREQ=YEARLY;INTERVAL=1;BYDATE=0704'); 
    DBMS_SCHEDULER.CREATE_SCHEDULE('CHRISTMAS_DAY', repeat_interval => 'FREQ=YEARLY;INTERVAL=1;BYDATE=1225'); 
    DBMS_SCHEDULER.CREATE_SCHEDULE('SPRING_BREAK', repeat_interval => 'FREQ=YEARLY;BYDATE=0301+SPAN:7D'); 
END; 

または別の例:

BEGIN 
    DBMS_SCHEDULER.CREATE_SCHEDULE('New_Year', repeat_interval => 'FREQ=YEARLY;BYDATE=0101'); 

    DBMS_SCHEDULER.CREATE_SCHEDULE('Easter_Sunday', repeat_interval => 'FREQ=YEARLY;BYDATE=20150405, 20160327, 20170416, 20170416, 20180401, 20190421, 20200412', comments => 'Hard coded till 2020'); 
    DBMS_SCHEDULER.CREATE_SCHEDULE('Good_Friday', repeat_interval => 'FREQ=YEARLY;BYDATE=20150405-2D, 20160327-2D, 20170416-2D, 20170416-2D, 20180401-2D, 20190421-2D, 20200412-2D', comments => '2 Days before Easter'); 
    DBMS_SCHEDULER.CREATE_SCHEDULE('Easter_Monday', repeat_interval => 'FREQ=YEARLY;BYDATE=20150405+1D, 20160327+1D, 20170416+1D, 20170416+1D, 20180401+1D, 20190421+1D, 20200412+1D', comments => '1 Day after Easter'); 
    DBMS_SCHEDULER.CREATE_SCHEDULE('Ascension_Day', repeat_interval => 'FREQ=YEARLY;BYDATE=20150405+39D,20160327+39D,20170416+39D,20170416+39D,20180401+39D,20190421+39D,20200412+39D', comments => '39 Days after Easter'); 
    DBMS_SCHEDULER.CREATE_SCHEDULE('Pentecost_Monday', repeat_interval => 'FREQ=YEARLY;BYDATE=20150405+50D,20160327+50D,20170416+50D,20170416+50D,20180401+50D,20190421+50D,20200412+50D', comments => '50 Days after easter'); 

    DBMS_SCHEDULER.CREATE_SCHEDULE('Repentance_and_Prayer', repeat_interval => 'FREQ=DAILY;BYDATE=1122-SPAN:7D;BYDAY=WED', 
     comments => 'Wednesday before November 23th, Buss- und Bettag'); 
    -- alternative solution: 
    --DBMS_SCHEDULER.CREATE_SCHEDULE('Repentance_and_Prayer', repeat_interval => 'FREQ=MONTHLY;BYMONTH=NOV;BYDAY=3 WED', 
    -- comments => '3rd Wednesday in November'); 

    DBMS_SCHEDULER.CREATE_SCHEDULE('Labor_Day', repeat_interval => 'FREQ=YEARLY;BYDATE=0501'); 
    DBMS_SCHEDULER.CREATE_SCHEDULE('German_Unity_Day', repeat_interval => 'FREQ=YEARLY;BYDATE=1003'); 
    DBMS_SCHEDULER.CREATE_SCHEDULE('Christmas', repeat_interval => 'FREQ=YEARLY;BYDATE=1225+SPAN:2D'); 

    DBMS_SCHEDULER.CREATE_SCHEDULE('Christian_Celebration_Days', repeat_interval => 'FREQ=DAILY;INTERSECT=Easter_Sunday,Good_Friday,Easter_Monday,Ascension_Day,Pentecost_Monday,Repentance_and_Prayer,Christmas'); 
    -- alternative solution: 
    -- DBMS_SCHEDULER.CREATE_SCHEDULE('Christian_Celebration_Days', repeat_interval => 'FREQ=Good_Friday;BYDAY=1 MON, 6 THU,8 MON'); 
    DBMS_SCHEDULER.CREATE_SCHEDULE('Political_Holidays', repeat_interval => 'FREQ=DAILY;INTERSECT=New_Year,Labor_Day,German_Unity_Day'); 

END; 
/

がここにカレンダーの構文を参照してください。Calendaring Syntax

を次にこれと同様の機能を作成します。

CREATE OR REPLACE FUNCTION GetBusinessHours(start_time IN TIMESTAMP, end_time IN TIMESTAMP) RETURN INTERVAL DAY TO SECOND AS 
    next_run_date TIMESTAMP := start_time; 
    duration INTERVAL DAY(3) TO SECOND(0) := INTERVAL '0' HOUR; 
BEGIN 
    LOOP 
     DBMS_SCHEDULER.EVALUATE_CALENDAR_STRING('FREQ=HOURLY;INTERVAL=1;BYHOUR=8,9,10,11,13,14,15,16,17;BYDAY=MON,TUE,WED,THU,FRI,SAT; EXCLUDE=NEW_YEARS_DAY,MARTIN_LUTHER_KING_DAY,WASHINGTONS_BIRTHDAY,MEMORIAL_DAY,INDEPENDENCE_DAY,CHRISTMAS_DAY,SPRING_BREAK', NULL, next_run_date, next_run_date); 
     duration := duration + INTERVAL '1' HOUR; 
     EXIT WHEN next_run_date >= end_time; 
    END LOOP; 
    RETURN duration; 
END; 

CREATE OR REPLACE FUNCTION GetBusinessStart(start_time IN TIMESTAMP, end_time IN TIMESTAMP) RETURN TIMESTAMP AS 
    next_run_date TIMESTAMP := start_time; 
BEGIN 
    DBMS_SCHEDULER.EVALUATE_CALENDAR_STRING('FREQ=HOURLY;INTERVAL=1;BYHOUR=8,9,10,11,13,14,15,16,17;BYDAY=MON,TUE,WED,THU,FRI,SAT; EXCLUDE=NEW_YEARS_DAY,MARTIN_LUTHER_KING_DAY,WASHINGTONS_BIRTHDAY,MEMORIAL_DAY,INDEPENDENCE_DAY,CHRISTMAS_DAY,SPRING_BREAK', NULL, next_run_date, next_run_date); 
    RETURN next_run_date; 
END; 


CREATE OR REPLACE FUNCTION GetBusinessEnd(start_time IN TIMESTAMP, end_time IN TIMESTAMP) RETURN TIMESTAMP AS 
    next_run_date TIMESTAMP := start_time; 
BEGIN 
    LOOP 
     DBMS_SCHEDULER.EVALUATE_CALENDAR_STRING('FREQ=HOURLY;INTERVAL=1;BYHOUR=8,9,10,11,13,14,15,16,17;BYDAY=MON,TUE,WED,THU,FRI,SAT; EXCLUDE=NEW_YEARS_DAY,MARTIN_LUTHER_KING_DAY,WASHINGTONS_BIRTHDAY,MEMORIAL_DAY,INDEPENDENCE_DAY,CHRISTMAS_DAY,SPRING_BREAK', NULL, next_run_date, next_run_date); 
     EXIT WHEN next_run_date >= end_time; 
    END LOOP; 
    RETURN next_run_date; 
END; 

あなたが考慮する必要がない場合r公立の祝日、ちょうどEXCLUDE=...部分をスキップします。あなたは、クエリ内の関数を使用することができます

その後:

SELECT TASK, 
    GetBusinessStart(START_TIME, END_TIME), 
    GetBusinessEnd(START_TIME, END_TIME), 
    GetBusinessHours(START_TIME, END_TIME) 
FROM ...; 

注意、機能は同じ非稼働日に両方を落ちる場合START_TIMEとEND_TIMEにいくつかの微調整が必​​要になります。

+0

あなたの応答のためにありがとう、ありがとう私の結果を見て、投稿する.. – user3191160

関連する問題