在不创建函数或视图的情况下计算两个日期之间的营业时间

时间:2019-06-03 16:14:54

标签: sql oracle date

我意识到这可能是一个多余的问题,但是我一直努力遵循我确实找到的一些示例,我想我会再次询问以提供特定情况的详细信息。

这就是我与之合作的原因:

  • Oracle数据库
  • 日期为时间戳格式
  • 由于权限问题,我无法创建任何其他表/视图
  • 由于权限问题,我无法创建任何自定义功能
  • 我每周工作40小时,周一至周五的工作时间为8到4:30。 (从技术上讲,我认为我们有40多个小时的时间来处理b / c,我不想花那么多时间担心不用考虑午休时间)

我能够计算出工作时间,但是我不知道如何进入工作日部分。

1 个答案:

答案 0 :(得分:2)

从您的星期五上午8点到星期一上午9点​​开始:

with dates as (
 Select timestamp '2019-05-31 08:00:00' start_date
      , timestamp '2019-06-03 09:00:00' end_date
   from dual
)

我们需要生成介于两者之间的日期。我们可以通过递归查询来做到这一点:

, recur(start_date, calc_date, end_date) as (
  -- Anchor Part
  select start_date
       , trunc(start_date)
       , end_date
    from dates

  -- Recrusive Part
  union all
  select start_date
       , calc_date+1
       , end_date
    from recur
   where calc_date+1 < end_Date
)

由此,我们需要弄清一些事情,例如,工作日是一个工作日还是一个周末,以及calc_day的开始和结束时间是多少,我们可以采用这些值并使用一点日期算法来查找当天的工作小时数(从我们开始使用时间戳开始,将天数返回到第二个间隔):

, days as (
  select calc_date
       , case when mod(to_number(to_char(calc_date,'d'))-1,6) != 0 then 1 end isWeekDay
       , greatest(start_date, calc_date + interval '8' hour) start_time
       , least(end_date, calc_date      + interval '16:30' hour to minute) end_time

       , least( ( least(end_date, calc_date      + interval '16:30' hour to minute) 
                - greatest(start_date, calc_date + interval '8' hour)
                ) * case when mod(to_number(to_char(calc_date,'d'))-1,6) != 0 then 1 end
              , interval '8' hour
         ) daily_hrs
    from recur
   where start_date < (calc_date + interval '16:30' hour to minute)
     and (calc_date + interval '8' hour) < end_date
)

请注意,在上述步骤中,我们将每天的工作时间限制为每天8小时,而where子句可防止在工作时间以外的开始或结束日期。最后一步是汇总小时数。不幸的是,Oracle没有任何本机间隔汇总或分析功能,但是我们仍然可以通过将间隔转换为秒,求和然后再将其转换回间隔进行输出来进行管理:

select calc_date
     , daily_hrs
     , numtodsinterval(sum( extract(hour   from daily_hrs)*60*60
                          + extract(minute from daily_hrs)*60
                          + extract(second from daily_hrs)
                          ) over (order by calc_date)
                      ,'second') run_sum
  from days;

我已经完成了上述总和作为分析函数,因此我们可以看到一些中间数据,但是如果您只想要最终输出,则可以将查询的最后一部分更改为此:

select numtodsinterval(sum( extract(hour from daily_hrs)*60*60
                          + extract(minute from daily_hrs)*60
                          + extract(second from daily_hrs)
                          )
                      ,'second') run_sum

这是一个 db<>fiddle ,整个查询都在起作用。请注意,在小提琴中,由于一周的第一天是特定于国家/地区的,因此我将数据库会话的NLS_TERRITORY设置更改为AMERICA以使查询正常工作。提琴中的第二个查询替换了特定于地区的功能:

case when mod(to_number(to_char(calc_date,'d'))-1,6) != 0 then 1 end

具有位置和语言不可知的计算:

case when (mod(mod(calc_date - next_day(date '2019-1-1',to_char(date '2019-01-06','day')),7),6)) != 0 then 1 end