【问题标题】:Oracle SQL: Time difference in hours, mins and secs between two days and break/aggregate by dateOracle SQL:两天之间的小时、分钟和秒的时差,并按日期中断/聚合
【发布时间】:2016-02-03 17:00:14
【问题描述】:

这是一个表格,其中包含员工的工作时间,该任务可能跨越也可能不跨越两天(或更长时间)。

需要帮助按每个员工的日期和任务 ID 分别以小时、分钟和秒为单位汇总时间。

表格示例:

Emp_ID | Task_ID | Task_start_date_time   | Task_stop_date_time  
 A1    | 123     | 01/13/2016 11:30:30 PM |  01/14/2016 12:30:30 AM  
 B1    | 124     | 01/16/2016 10:30:35 PM |  01/18/2016  2:30:35 AM  
 C1    | 125     | 01/19/2016  9:30:20 AM |  01/19/2016  2:30:20 PM  
 D1    | 126     | 01/20/2016  3:15:25 AM |  01/21/2016  2:25:25 PM  

所需的 SQL 结果集:

A1 | 123 | 01/13/2016 | 00:29:30  
A1 | 123 | 01/14/2016 | 04:30:30  
B1 | 124 | 01/16/2016 | 01:29:25  
B1 | 124 | 01/17/2016 | 24:00:00  
B1 | 124 | 01/18/2016 | 02:30:35  
C1 | 125 | 01/19/2016 | 05:00:00  
D1 | 126 | 01/20/2016 | 20:44:35  
D1 | 126 | 01/20/2016 | 14:25:25  

提前致谢。

补充:一个 eid 和 tid 有多行?并且需要以同样的方式聚合。

表格示例:

Emp_ID | Task_ID | Task_start_date_time   | Task_stop_date_time  
 A1    | 123     | 01/13/2016 11:30:30 PM |  01/14/2016 12:30:30 AM  
 A1    | 123     | 01/14/2016 10:30:35 AM |  01/14/2016 2:30:35 PM  
 B1    | 124     | 01/16/2016 10:30:35 PM |  01/18/2016  2:30:35 AM  
 C1    | 125     | 01/19/2016  9:30:20 AM |  01/19/2016  2:30:20 PM  
 D1    | 126     | 01/20/2016  3:15:25 AM |  01/21/2016  2:25:25 PM  

所需的 SQL 结果集:

A1 | 123 | 01/13/2016 | 00:29:30  
A1 | 123 | 01/14/2016 | 08:30:30  
B1 | 124 | 01/16/2016 | 01:29:25  
B1 | 124 | 01/17/2016 | 24:00:00  
B1 | 124 | 01/18/2016 | 02:30:35  
C1 | 125 | 01/19/2016 | 05:00:00  
D1 | 126 | 01/20/2016 | 20:44:35  

【问题讨论】:

  • 哎呀!表格和结果布局不正确。
  • 我的问题的“所需的 sql 结果集”部分也有错字。第二行应该是:A1 | 123 | 2016 年 1 月 14 日 | 00:30:30

标签: oracle date aggregate break


【解决方案1】:

一种可能性:

with data as (
  select emp_id eid, task_id tid, task_start_date_time d1, task_stop_date_time d2, 
         trunc(task_stop_date_time)-trunc(task_start_date_time)+1 cnt from tasks),
rec(eid, tid, d1, d2, cnt, rn) as (
  select eid, tid, d1, d2, cnt, 1 from data
  union all 
  select eid, tid, d1, d2, cnt, rn+1 from rec where rn+1<=cnt),
res as (
  select eid, tid, rn, trunc(d1)+rn-1 dt, 
         least(d2, trunc(d1)+rn) - greatest(d1, trunc(d1)+rn-1) diff 
    from rec)
select eid, tid, dt, 
       to_char(trunc(diff*24), 'fm00')||':'||
       to_char(trunc(mod(diff*24*60, 60)), 'fm00')||':'||
       to_char(trunc(mod(diff*24*60*60, 60 )), 'fm00') diff
  from res order by eid, rn

步骤:

  • 1 - 子查询 data - 我在这里添加了列 cnt,其中包含基于 start_date 和 end_date 之间差异的天数,
  • 2 - 递归子查询rec - 这会根据输入数据为每一行生成所需的行数,
  • 3 - 子查询res - 添加适当值的差异,此差异以天为单位,
  • 4 - 最后一个查询,将 diff 从天数转换为 hh:mm:ss 格式。

运行此查询需要 Oracle 11g。我将表命名为tasks,下面是示例数据和输出。

create table tasks (Emp_ID varchar2(2), Task_ID number(6), Task_start_date_time date, Task_stop_date_time date);
insert into tasks values ('A1', 123, timestamp '2016-01-13 23:30:30', timestamp '2016-01-14 00:30:30');
insert into tasks values ('B1', 124, timestamp '2016-01-16 22:30:35', timestamp '2016-01-18 02:30:35');
insert into tasks values ('C1', 125, timestamp '2016-01-19 09:30:20', timestamp '2016-01-19 14:30:20');
insert into tasks values ('D1', 126, timestamp '2016-01-20 03:15:25', timestamp '2016-01-21 14:25:25');

输出:

EID        TID DT       DIFF      
--- ---------- -------- -----------
A1         123 16/01/13 00:29:29    
A1         123 16/01/14 00:30:30    
B1         124 16/01/16 01:29:25    
B1         124 16/01/17 24:00:00    
B1         124 16/01/18 02:30:34    
C1         125 16/01/19 05:00:00    
D1         126 16/01/20 20:44:35    
D1         126 16/01/21 14:25:25 

更新:如果您想对值进行分组,正如您在 cmets 中提到的,那么您需要为每一天添加 group by 子句 - 请参阅子查询 res 和最终选择中的更改。

insert into tasks values ('A1', 123, 
                          timestamp '2016-01-14 10:30:35', 
                          timestamp '2016-01-14 14:30:35');

with data as (
  select emp_id eid, task_id tid, task_start_date_time d1, task_stop_date_time d2, 
         trunc(task_stop_date_time)-trunc(task_start_date_time)+1 cnt from tasks),
rec(eid, tid, d1, d2, cnt, rn) as (
  select eid, tid, d1, d2, cnt, 1 from data
  union all 
  select eid, tid, d1, d2, cnt, rn+1 from rec where rn+1<=cnt),
res as (
  select eid, tid, trunc(d1)+rn-1 dt, 
         sum(least(d2, trunc(d1)+rn) - greatest(d1, trunc(d1)+rn-1)) diff 
    from rec group by eid, tid, trunc(d1)+rn-1)
select eid, tid, dt, 
       to_char(trunc(diff*24), 'fm00')||':'||
       to_char(trunc(mod(diff*24*60, 60)), 'fm00')||':'||
       to_char(trunc(mod(diff*24*60*60, 60 )), 'fm00') diff
  from res order by eid, tid, dt

输出(更改在第 2 行:数据第一行 30 分 30 秒,第二行 4 小时):

EID        TID DT          DIFF
--- ---------- ----------- -----------
A1         123 2016-01-13  00:29:29
A1         123 2016-01-14  04:30:30
B1         124 2016-01-16  01:29:25
B1         124 2016-01-17  24:00:00
B1         124 2016-01-18  02:30:34
C1         125 2016-01-19  05:00:00
D1         126 2016-01-20  20:44:35
D1         126 2016-01-21  14:25:25

【讨论】:

  • 太棒了,效果很好!细想,感谢及时准确的回复!!!
  • 一个补充:一个eid和tid有多行?并且需要以同样的方式聚合。示例表条目: Emp_ID |任务 ID |任务开始日期时间 |任务停止日期时间 A1 | 123 | 2016 年 1 月 13 日晚上 11:30:30 | 2016 年 1 月 14 日上午 12:30:30 || A1 | 123 | 2016 年 1 月 14 日上午 10:30:35 | 2016 年 1 月 14 日下午 2:30:35
  • @KumarSambhuni - 请edit您的原始问题添加此示例。这一点很重要。将来请提供 DDL 和 DML 语句(类似于我在答案中包含的这些语句),这对于试图提供帮助的人来说是一个很好的起点。另请阅读how-to-ask
  • 抱歉,不知道规则。新发布关于堆栈溢出的问题。下次会注意的。
  • 没问题,您的问题仍然悬而未决。我在想如果简单的group by 不能解决它?但是请添加此示例,也许有人会找到更好的解决方案。
猜你喜欢
  • 2012-05-19
  • 2020-11-25
  • 1970-01-01
  • 2011-07-29
  • 2014-01-17
  • 1970-01-01
  • 1970-01-01
  • 2021-12-15
  • 1970-01-01
相关资源
最近更新 更多