计算通过的服务时间

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。

以上是关于计算通过的服务时间的主要内容,如果未能解决你的问题,请参考以下文章

安全可信 | 增强级认定!天翼云政务云通过云计算服务安全评估

云计算带来的好处

ROS 计算图级,理解ROS 节点、话题

全部满分!阿里云函数计算通过可信云 21 项测试

无法通过 SSH 连接到家用计算机 [关闭]

如何通过使用 Selenium Grid 将文件从本地计算机传输到远程 Web 服务器来上传文件