计算通过的服务时间
Posted
技术标签:
【中文标题】计算通过的服务时间【英文标题】:Calculating passed service hours 【发布时间】:2021-01-15 12:32:22 【问题描述】:我正在尝试根据服务时间计算自记录服务请求以来经过的时间(服务时间)。
开始时间是记录工单的时间 (date_logged),结束时间可以是当前时间(对于打开的工单)或 date_closed 对于已关闭的工单。
服务时间因工单被分配到的部门而异,如下例所示(我表中的所有时间和日期都具有数据类型 datetime ):
Ticket-Nr. | Department | date_logged | date_closed | start_mon | end_mon | start_tue | end_tue | start_wed | end_wed | start_trs | end_trs | start_fri | end_fri | start_sat | end_sat |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
1234567 | A | 06.01.21 11:30:52 | 01.01.2001 07:30 | 01.01.2001 16:45 | 01.01.2001 07:30 | 01.01.2001 16:45 | 01.01.2001 07:30 | 01.01.2001 16:45 | 01.01.2001 07:30 | 01.01.2001 16:45 | 01.01.2001 07:30 | 01.01.2001 13:00 | |||
8912345 | B | 13.01.21 09:14:16 | 01.01.2001 07:00 | 01.01.2001 16:30 | 01.01.2001 07:00 | 01.01.2001 16:30 | 01.01.2001 07:00 | 01.01.2001 16:30 | 01.01.2001 07:00 | 01.01.2001 16:30 | 01.01.2001 07:00 | 01.01.2001 16:30 | 01.01.2001 07:00 | 01.01.2001 15:00 | |
6789012 | C | 14.01.21 10:48:01 | 14.01.21 11:40 | 01.01.2001 07:00 | 01.01.2001 16:30 | 01.01.2001 07:00 | 01.01.2001 16:30 | 01.01.2001 07:00 | 01.01.2001 16:30 | 01.01.2001 07:00 | 01.01.2001 16:30 | 01.01.2001 07:00 | 01.01.2001 16:30 | 01.01.2001 07:00 | 01.01.2001 15:00 |
3456789 | D | 15.01.2021 09:41:00 | 01.01.2001 08:00 | 01.01.2001 15:00 | 01.01.2001 08:00 | 01.01.2001 15:00 | 01.01.2001 08:00 | 01.01.2001 15:00 | 01.01.2001 08:00 | 01.01.2001 15:00 | 01.01.2001 08:00 | 01.01.2001 13:00 | |||
0123456 | B | 02.01.2021 13:12:00 | ... | ... | ... | ... | ... |
结果应该看起来像这样(减去“当前日期时间”列,这只是为了上下文):
Ticket-Nr. | department | date_logged | service time [hh:mm] | current datetime | date_closed |
---|---|---|---|---|---|
1234567 | A | 06.01.21 11:30:52 | 62:33 | 14.01.2021 12:03 | |
8912345 | B | 13.01.21 09:14:16 | 12:19 | 14.01.2021 12:03 | |
6789012 | C | 14.01.21 10:48:01 | 00:28 | 14.01.2021 12:03 | 14.01.21 11:40 |
... | ... | ... | ... | ... | ... |
A、B、D部门必须包括公共假期。
【问题讨论】:
鉴于您的业务规则的复杂性,我建议您创建每天一行和部门的表格,指定当天的工作时间。 @GordonLinoff 好点!我希望现在更清楚了。 如果你没有太多的部门那么你仍然可以使用我的方法:***.com/questions/41936398/… 请edit 使用minimal reproducible example 提出您的问题,包括DDL (CREATE TABLE
) 语句或您的表和DML (INSERT
) 语句用于您的示例数据。虽然表格可能看起来不错,但对于回答问题的人来说,必须将表格中的所有数据提取到 INSERT
语句中并猜测您的数据类型是没有帮助的。
另外,假设一个部门对所有工单的服务时间始终相同,看起来您的更新违反了第 3 范式,并且您将在每一行。最好回到你以前的结构并有一个单独的小时表。
【参考方案1】:
您可以直接计算小时数(扩展您的previous question):
SELECT ticket_nr,
department,
date_logged,
current_datetime,
date_closed,
TO_CHAR( FLOOR( service_time_seconds / 60 / 60 ), 'FM99990' )
|| ':'
|| TO_CHAR( MOD( FLOOR( service_time_seconds / 60 ), 60 ), 'FM00' )
|| ':'
|| TO_CHAR( MOD( service_time_seconds, 60 ), 'FM00' )
AS "SERVICE_TIME HH:MM:SS"
FROM (
SELECT t.ticket_nr,
t.department,
t.date_logged,
SYSDATE AS current_datetime,
t.date_closed,
ROUND(
(
-- Calculate the full weeks difference from the start of ISO weeks.
(
TRUNC( COALESCE( date_closed, SYSDATE ), 'IW' )
- TRUNC( date_logged, 'IW' )
) * ( s.hours_mon
+ s.hours_tue
+ s.hours_wed
+ s.hours_thu
+ s.hours_fri
+ s.hours_sat
+ s.hours_sun ) / (7*24)
-- Add the hours for the full days for the final week.
+ DECODE(
TRUNC( COALESCE( date_closed, SYSDATE ) )
- TRUNC( COALESCE( date_closed, SYSDATE ), 'IW' ),
0, 0.0,
1, s.hours_mon,
2, s.hours_mon + s.hours_tue,
3, s.hours_mon + s.hours_tue + s.hours_wed,
4, s.hours_mon + s.hours_tue + s.hours_wed + s.hours_thu,
5, s.hours_mon + s.hours_tue + s.hours_wed + s.hours_thu + s.hours_fri,
6, s.hours_mon + s.hours_tue + s.hours_wed + s.hours_thu + s.hours_fri + s.hours_sat
) / 24
-- Subtract the hours for the full days from the days of the week
-- before the date logged.
- DECODE(
TRUNC( date_logged ) - TRUNC( date_logged, 'IW' ),
0, 0.0,
1, s.hours_mon,
2, s.hours_mon + s.hours_tue,
3, s.hours_mon + s.hours_tue + s.hours_wed,
4, s.hours_mon + s.hours_tue + s.hours_wed + s.hours_thu,
5, s.hours_mon + s.hours_tue + s.hours_wed + s.hours_thu + s.hours_fri,
6, s.hours_mon + s.hours_tue + s.hours_wed + s.hours_thu + s.hours_fri + s.hours_sat
) / 24
-- Add the hours of the final day
+ COALESCE(
GREATEST(
LEAST(
COALESCE( date_closed, SYSDATE ),
TRUNC( COALESCE( date_closed, SYSDATE ) )
+ DECODE(
TRUNC( COALESCE( date_closed, SYSDATE ) )
- TRUNC( COALESCE( date_closed, SYSDATE ), 'IW' ),
0, s.end_mon,
1, s.end_tue,
2, s.end_wed,
3, s.end_thu,
4, s.end_fri,
5, s.end_sat,
6, s.end_sun
)
)
-
(
TRUNC( COALESCE( date_closed, SYSDATE ) )
+ DECODE(
TRUNC( COALESCE( date_closed, SYSDATE ) )
- TRUNC( COALESCE( date_closed, SYSDATE ), 'IW' ),
0, s.start_mon,
1, s.start_tue,
2, s.start_wed,
3, s.start_thu,
4, s.start_fri,
5, s.start_sat,
6, s.start_sun
)
),
0
) / 24,
0
)
-- Subtract the hours of the day before the range starts.
+ COALESCE(
GREATEST(
LEAST(
date_logged,
date_logged
+ DECODE(
TRUNC( COALESCE( date_closed, SYSDATE ) )
- TRUNC( COALESCE( date_closed, SYSDATE ), 'IW' ),
0, s.end_mon,
1, s.end_tue,
2, s.end_wed,
3, s.end_thu,
4, s.end_fri,
5, s.end_sat,
6, s.end_sun
)
)
-
(
date_logged
+ DECODE(
TRUNC( COALESCE( date_closed, SYSDATE ) )
- TRUNC( COALESCE( date_closed, SYSDATE ), 'IW' ),
0, s.start_mon,
1, s.start_tue,
2, s.start_wed,
3, s.start_thu,
4, s.start_fri,
5, s.start_sat,
6, s.start_sun
)
),
0
) / 24,
0
)
)
-- Multiply to give seconds rather than fractions of full days.
* 24 * 60 * 60
) AS service_time_seconds
FROM table_name t
INNER JOIN service_hours s
ON ( s.department = t.department )
);
其中,对于样本数据:
CREATE TABLE table_name ( Ticket_Nr, department, date_logged, date_closed ) AS
SELECT 1234567, 'A', DATE '2021-01-06' + INTERVAL '11:30:52' HOUR TO SECOND, NULL FROM DUAL UNION ALL
SELECT 8912345, 'B', DATE '2021-01-13' + INTERVAL '09:14:16' HOUR TO SECOND, NULL FROM DUAL UNION ALL
SELECT 6789012, 'C', DATE '2021-01-14' + INTERVAL '10:48:28' HOUR TO SECOND, DATE '2021-01-21' + INTERVAL '11:40:00' HOUR TO SECOND FROM DUAL UNION ALL
SELECT 1, 'D', DATE '2021-01-07' + INTERVAL '07:00:00' HOUR TO SECOND, DATE '2021-01-14' + INTERVAL '07:00:00' HOUR TO SECOND FROM DUAL UNION ALL
SELECT 2, 'A', DATE '2021-01-07' + INTERVAL '07:00:00' HOUR TO SECOND, DATE '2021-01-08' + INTERVAL '07:00:00' HOUR TO SECOND FROM DUAL UNION ALL
SELECT 3, 'A', DATE '2021-01-08' + INTERVAL '07:00:00' HOUR TO SECOND, DATE '2021-01-09' + INTERVAL '07:00:00' HOUR TO SECOND FROM DUAL UNION ALL
SELECT 4, 'A', DATE '2021-01-09' + INTERVAL '07:00:00' HOUR TO SECOND, DATE '2021-01-10' + INTERVAL '07:00:00' HOUR TO SECOND FROM DUAL UNION ALL
SELECT 5, 'B', DATE '2021-01-09' + INTERVAL '07:00:00' HOUR TO SECOND, DATE '2021-01-10' + INTERVAL '07:00:00' HOUR TO SECOND FROM DUAL;
CREATE TABLE service_hours (
Department VARCHAR2(5)
CONSTRAINT service_hours__department__pk PRIMARY KEY,
start_mon INTERVAL DAY TO SECOND,
end_mon INTERVAL DAY TO SECOND,
hours_mon NUMBER
GENERATED ALWAYS AS (
COALESCE(
( DATE '1970-01-01' + end_mon ) - ( DATE '1970-01-01' + start_mon ),
0
) * 24
),
start_tue INTERVAL DAY TO SECOND,
end_tue INTERVAL DAY TO SECOND,
hours_tue NUMBER
GENERATED ALWAYS AS (
COALESCE(
( DATE '1970-01-01' + end_tue ) - ( DATE '1970-01-01' + start_tue ),
0
) * 24
),
start_wed INTERVAL DAY TO SECOND,
end_wed INTERVAL DAY TO SECOND,
hours_wed NUMBER
GENERATED ALWAYS AS (
COALESCE(
( DATE '1970-01-01' + end_wed ) - ( DATE '1970-01-01' + start_wed ),
0
) * 24
),
start_thu INTERVAL DAY TO SECOND,
end_thu INTERVAL DAY TO SECOND,
hours_thu NUMBER
GENERATED ALWAYS AS (
COALESCE(
( DATE '1970-01-01' + end_thu ) - ( DATE '1970-01-01' + start_thu ),
0
) * 24
),
start_fri INTERVAL DAY TO SECOND,
end_fri INTERVAL DAY TO SECOND,
hours_fri NUMBER
GENERATED ALWAYS AS (
COALESCE(
( DATE '1970-01-01' + end_fri ) - ( DATE '1970-01-01' + start_fri ),
0
) * 24
),
start_sat INTERVAL DAY TO SECOND,
end_sat INTERVAL DAY TO SECOND,
hours_sat NUMBER
GENERATED ALWAYS AS (
COALESCE(
( DATE '1970-01-01' + end_sat ) - ( DATE '1970-01-01' + start_sat ),
0
) * 24
),
start_sun INTERVAL DAY TO SECOND,
end_sun INTERVAL DAY TO SECOND,
hours_sun NUMBER
GENERATED ALWAYS AS (
COALESCE(
( DATE '1970-01-01' + end_sun ) - ( DATE '1970-01-01' + start_sun ),
0
) * 24
)
);
INSERT INTO service_hours (
Department,
start_mon, end_mon,
start_tue, end_tue,
start_wed, end_wed,
start_thu, end_thu,
start_fri, end_fri,
start_sat, end_sat,
start_sun, end_sun
)
SELECT 'A',
INTERVAL '07:30' HOUR TO MINUTE, INTERVAL '16:45' HOUR TO MINUTE,
INTERVAL '07:30' HOUR TO MINUTE, INTERVAL '16:45' HOUR TO MINUTE,
INTERVAL '07:30' HOUR TO MINUTE, INTERVAL '16:45' HOUR TO MINUTE,
INTERVAL '07:30' HOUR TO MINUTE, INTERVAL '16:45' HOUR TO MINUTE,
INTERVAL '07:30' HOUR TO MINUTE, INTERVAL '13:00' HOUR TO MINUTE,
NULL, NULL,
NULL, NULL
FROM DUAL UNION ALL
SELECT 'B',
INTERVAL '07:00' HOUR TO MINUTE, INTERVAL '16:30' HOUR TO MINUTE,
INTERVAL '07:00' HOUR TO MINUTE, INTERVAL '16:30' HOUR TO MINUTE,
INTERVAL '07:00' HOUR TO MINUTE, INTERVAL '16:30' HOUR TO MINUTE,
INTERVAL '07:00' HOUR TO MINUTE, INTERVAL '16:30' HOUR TO MINUTE,
INTERVAL '07:00' HOUR TO MINUTE, INTERVAL '16:30' HOUR TO MINUTE,
INTERVAL '07:00' HOUR TO MINUTE, INTERVAL '15:00' HOUR TO MINUTE,
NULL, NULL
FROM DUAL UNION ALL
SELECT 'C',
INTERVAL '07:00' HOUR TO MINUTE, INTERVAL '16:30' HOUR TO MINUTE,
INTERVAL '07:00' HOUR TO MINUTE, INTERVAL '16:30' HOUR TO MINUTE,
INTERVAL '07:00' HOUR TO MINUTE, INTERVAL '16:30' HOUR TO MINUTE,
INTERVAL '07:00' HOUR TO MINUTE, INTERVAL '16:30' HOUR TO MINUTE,
INTERVAL '07:00' HOUR TO MINUTE, INTERVAL '16:30' HOUR TO MINUTE,
INTERVAL '07:00' HOUR TO MINUTE, INTERVAL '15:00' HOUR TO MINUTE,
NULL, NULL
FROM DUAL UNION ALL
SELECT 'D',
INTERVAL '08:00' HOUR TO MINUTE, INTERVAL '15:00' HOUR TO MINUTE,
INTERVAL '08:00' HOUR TO MINUTE, INTERVAL '15:00' HOUR TO MINUTE,
INTERVAL '08:00' HOUR TO MINUTE, INTERVAL '15:00' HOUR TO MINUTE,
INTERVAL '08:00' HOUR TO MINUTE, INTERVAL '15:00' HOUR TO MINUTE,
INTERVAL '08:00' HOUR TO MINUTE, INTERVAL '15:00' HOUR TO MINUTE,
NULL, NULL,
NULL, NULL
FROM DUAL;
输出:
TICKET_NR |部门 | DATE_LOGGED |当前日期时间 | DATE_CLOSED | SERVICE_TIME HH:MM:SS --------: | :--------- | :------------------------ | :------------------------ | :------------------------ | :-------------------- 1234567 |一个 | 2021-01-06 11:30:52 (星期三) | 2021-01-15 13:38:49 (周五) | 空 | 61:13:45 8912345 |乙| 2021-01-13 09:14:16 (星期三) | 2021-01-15 13:38:49 (周五) | 空 | 19:16:37 6789012 | C | 2021-01-14 10:48:28 (星期四) | 2021-01-15 13:38:49 (周五) | 2021-01-21 11:40:00 (星期四) | 55:41:40 1 | D | 2021-01-07 07:00:00 (星期四) | 2021-01-15 13:38:49 (周五) | 2021-01-14 07:00:00 (星期四) | 35:00:00 2 |一个 | 2021-01-07 07:00:00 (星期四) | 2021-01-15 13:38:49 (周五) | 2021-01-08 07:00:00 (周五) | 9:15:00 3 |一个 | 2021-01-08 07:00:00 (周五) | 2021-01-15 13:38:49 (周五) | 2021-01-09 07:00:00 (周六) | 5:30:00 4 |一个 | 2021-01-09 07:00:00 (周六) | 2021-01-15 13:38:49 (周五) | 2021-01-10 07:00:00 (星期日) | 0:00:00 5 |乙| 2021-01-09 07:00:00 (周六) | 2021-01-15 13:38:49 (周五) | 2021-01-10 07:00:00 (星期日) | 8:00:00
db小提琴here
【讨论】:
非常感谢您的回答!不幸的是,我意识到我对表格结构的解释不够充分......服务时间实际上与门票在同一个表格中。我已经编辑了我原来的问题,所以你知道它是什么样子的。我尝试通过将 s.hours_day 与 24*(day_end - day_start) 交换来更改您的脚本,但我收到一个错误,告诉我“不允许日期 + 日期”(day_start 和 day_end 列具有日期时间格式)。再次感谢您,给您带来的不便深表歉意。 @Acinogara 变化不大db<>fiddle。以上是关于计算通过的服务时间的主要内容,如果未能解决你的问题,请参考以下文章