我希望根据营业时间计算任务开始和结束之间的小时数。我有以下示例数据:
export function toggleDrawer (args: EventData) {
//this way
let page = <Page>(<any>args.object).page;
//or this way
let page = frameModule.topmost();
//at last get drawer content at page
let drawer: any = page.getViewById("sideDrawer");
drawer.toggleDrawerState();
};
我需要根据以下营业时间计算两者之间的差异:
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
我知道如何编写计算但不知道如何将营业时间添加到计算中。
任何建议都将受到赞赏。
答案 0 :(得分:0)
使用相关的分层查询为每个工作日生成一行,然后将每天的小时数相加:
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;
答案 1 :(得分: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;
如果您不必考虑公共假期,请跳过EXCLUDE=...
部分。
然后您可以在查询中使用该功能:
SELECT TASK,
GetBusinessStart(START_TIME, END_TIME),
GetBusinessEnd(START_TIME, END_TIME),
GetBusinessHours(START_TIME, END_TIME)
FROM ...;
注意,如果START_TIME和END_TIME都落入同一个非工作日,该功能需要进行一些微调。