Oracle实现两个日期之间工单处理时长,剔除非工作时间,周末和法定节假日

本文介绍了如何使用Oracle SQL来计算两个日期间工单处理时长,同时排除周末和法定节假日。在实现过程中,考虑了节假日表t_work_holiday的数据,确保准确剔除非工作时间。

–实现处理时间统计函数,剔除非工作时间,周末和法定节假日
由于法定节假日没有固定的表,需要引入节假日表:t_work_holiday

create table T_WORK_HOLIDAY
(
  day       DATE not null,
  isholiday VARCHAR2(1) default 'H' not null
);
-- Add comments to the columns 
comment on column T_WORK_HOLIDAY.isholiday
  is 'H:Holiday,  W:Workday';
-- Create/Recreate primary, unique and foreign key constraints 
alter table T_WORK_HOLIDAY
  add constraint PK primary key (DAY)insert into t_work_holiday (DAY, ISHOLIDAY)
values (to_date('26-04-2020', 'dd-mm-yyyy'), 'W');

insert into t_work_holiday (DAY, ISHOLIDAY)
values (to_date('09-05-2020', 'dd-mm-yyyy'), 'W');

insert into t_work_holiday (DAY, ISHOLIDAY)
values (to_date('01-05-2020', 'dd-mm-yyyy'), 'H');

insert into t_work_holiday (DAY, ISHOLIDAY)
values (to_date('02-05-2020', 'dd-mm-yyyy'), 'H');

insert into t_work_holiday (DAY, ISHOLIDAY)
values (to_date('03-05-2020', 'dd-mm-yyyy'), 'H');

insert into t_work_holiday (DAY, ISHOLIDAY)
values (to_date('04-05-2020', 'dd-mm-yyyy'), 'H');

insert into t_work_holiday (DAY, ISHOLIDAY)
values (to_date('05-05-2020', 'dd-mm-yyyy'), 'H');
CREATE OR REPLACE FUNCTION get_finish_time(p_dayBegin in date,
                                      p_dayEnd   in date,
                                      p_num      in number)
/************************************************************/
  /*  funcate     : get_finish_time
  /*  Description : 计算工作小时数
                  : 工作日:早9:00~晚18:00 共计9小时
                  : 推算公式: 间隔天数超过1天:当前的时间+结束那天的时间+9*工作日间隔天数
                              当天处理完:结束时间-开始时间
  /*  Parameters  : p_dayBegin  开始时间
                    p_dayEnd    结束时间
                    p_num  保留位数
  */
 return number is
  minWorkday  date := p_dayBegin; --派单时间
  maxWorkDay  date := p_dayEnd; --当前时间
  v_dayBegin  date; --处理后的派单时间
  v_dayEnd    date; --处理后的当前时间
  v_num       number := p_num; --小数保留位数
  v_workBegin number := 1; --开始工作时间
  v_workEnd   number := 1; --结束工作时间
  v_calcHour  number; --总相差小时数
  days        number := 0; --间隔天数
  v_hour      number; --实际相差小时数

  isHoliday varchar2(1); --是否为节假日标签
  d         date; --临时变量

begin
  --计算工作时间
  v_workBegin := 9 / 24;
  v_workEnd   := 18 / 24;
  --找到第一个工作日
  for i in 0 .. TRUNC(p_dayEnd) - TRUNC(p_dayBegin) loop
    d := minWorkDay + i;
    select nvl((select t.isholiday
                 from t_work_holiday t
                where t.day = trunc(d)),
               case to_char(d, 'd')
                 when '1' then
                  'H'
                 when '7' then
                  'H'
                 else
                  'W'
               end)
      into isHoliday
      from sys.dual;
    if (isHoliday = 'W') then
      minWorkday := d;
      exit;
    else
      minWorkday := TRUNC(minWorkday) + v_workBegin;
    end if;
  end loop;

  --找到最后一个工作日
  for i in 0 .. TRUNC(p_dayEnd) - TRUNC(p_dayBegin) loop
    d := maxWorkDay - i;
    select nvl((select t.isholiday
                 from t_work_holiday t
                where t.day = trunc(d)),
               case to_char(d, 'd')
                 when '1' then
                  'H'
                 when '7' then
                  'H'
                 else
                  'W'
               end)
      into isHoliday
      from sys.dual;
    if (isHoliday = 'W') then
      maxWorkDay := d;
      exit;
    else
      maxWorkDay := TRUNC(maxWorkDay) + v_workEnd;
    end if;
  end loop;
  dbms_output.put_line(to_char(minWorkDay, 'yyyymmdd hh24:mi:ss'));
  dbms_output.put_line(to_char(maxWorkDay, 'yyyymmdd hh24:mi:ss'));

  --没有找到工作日
  if (maxWorkDay < minWorkDay) or isHoliday = 'H' then
    return(0);
  end if;

  --进行判断处理派单时间
  if minWorkday > (TRUNC(minWorkday) + v_workEnd) THEN
    --晚上工作时间以后
    v_dayBegin := (TRUNC(minWorkday) + v_workEnd); --变成当天晚上工作时间
  elsif minWorkday < (TRUNC(minWorkday) + v_workBegin) THEN
    --早上工作时间之前
    v_dayBegin := (TRUNC(minWorkday) + v_workBegin); --变成当天早上工作时间
  else
    v_dayBegin := minWorkday; --工作时间
  end if;
  --进行判断处理当前时间
  if maxWorkDay > (TRUNC(maxWorkDay) + v_workEnd) THEN
    --晚上工作时间之后
    v_dayEnd := (TRUNC(maxWorkDay) + v_workEnd); --变成当天晚上工作时间
  elsif maxWorkDay < (TRUNC(maxWorkDay) + v_workBegin) THEN
    --早上工作时间之前
    v_dayEnd := (TRUNC(maxWorkDay) + v_workBegin); --变成当天早上工作时间
  else
    v_dayEnd := maxWorkDay; --工作时间
  end if;
  dbms_output.put_line('v_dayBegin:' ||
                       to_char(v_dayBegin, 'yyyymmdd hh24:mi:ss'));
  dbms_output.put_line('v_dayEnd:' ||
                       to_char(v_dayEnd, 'yyyymmdd hh24:mi:ss'));
  --计算第一个工作日和最后一个工作日之间的工作日数量
  select sum(nvl(case b.isholiday
                   when 'H' then
                    0
                   when 'W' then
                    1
                   else
                    null
                 end,
                 case to_char(a.DAY_ID, 'd')
                   when '1' then
                    0
                   when '7' then
                    0
                   else
                    1
                 end))
    into days
    from (SELECT trunc(v_dayBegin) + ROWNUM - 1 DAY_ID
            FROM DUAL
          CONNECT BY ROWNUM <= trunc(v_dayEnd) - trunc(v_dayBegin) + 1) a
    left join t_work_holiday b
      on a.DAY_ID = b.day;

  dbms_output.put_line('workdays1:' || days);

  --计算时间差
  if (trunc(v_dayEnd) = trunc(v_dayBegin)) then
    --当天的情况
    v_calcHour := to_number(v_dayEnd - v_daybegin) * 24;
  else
    --隔天的情况
    v_calcHour := to_number((trunc(v_daybegin) + v_workEnd) - v_daybegin) * 24 +
                  to_number(v_dayEnd - (trunc(v_dayEnd) + v_workBegin)) * 24;
  end if;
  dbms_output.put_line('v_calcHour:' || v_calcHour);
  --计算工作小时数
  if days = 1 then
    v_hour := round(v_calcHour, v_num);
  else
    v_hour := round(v_calcHour + 9 * (days - 2), v_num);
  end if;
  return v_hour;
end get_finish_time;
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值