在oracle sql中添加工作日

时间:2018-10-15 16:32:06

标签: sql oracle

我有两个日期字段,DATE_FIELD_ONE = 8/30/2018和DATE_FIELD_TWO = DATE_FIELD_ONE +20。如果仅添加20个工作日,我需要查找DATE_FIELD_TWO应该是什么。我将如何完成?我以为可能尝试“ DY”,但不确定如何使它工作。谢谢。

CASE WHEN TO_CHAR(TO_DATE(DATE_FIELD_ONE),'DY')='SAT' THEN 1 ELSE 0 END
CASE WHEN TO_CHAR(TO_DATE(DATE_FIELD_ONE),'DY')='SUN' THEN 1 ELSE 0 END

3 个答案:

答案 0 :(得分:1)

您可以尝试以下方法:

select max(date_field_two) as date_field_two
  from
 (
 select date'2018-08-30'+  
    cast(case when to_char(date'2018-08-30'+level,'D','NLS_DATE_LANGUAGE=ENGLISH') 
                                              in ('6','7') then 
            0
          else
            level
          end as int) as date_field_two, 
 sum(cast(case when to_char(date'2018-08-30'+level,'D','NLS_DATE_LANGUAGE=ENGLISH')  
                                               in ('6','7') then 
            0
          else
            1
          end as int)) over (order by level) as next_day
      from dual
    connect by level <= 20*1.5 
-- 20 is the day to be added, every time 5(#of business days)*1.5 > 7(#of week days)
-- 7=5+2<5+(5/2)=5*(1+1/2)=5*1.5 [where 1.5 is just a coefficient might be replaced a greater one like 2]
-- so 4*5*1.5=20*1.5 > 4*7 
  )    
 where next_day = 20;

 DATE_FIELD_TWO
-----------------
   27.09.2018

通过使用connect by dual子句。

P.S。忽略公共假期的情况,公共假期因一种文化与另一种文化而异,具体取决于仅与周末有关的问题。

Rextester Demo

编辑: 假设您在“ 2018-09-25”和“ 2018-09-26”(在此天数)中有国定假日,则考虑以下内容:

select max(date_field_two) as date_field_two
  from
 (
 select date'2018-08-30'+  
        (case when to_char(date'2018-08-30'+level,'D','NLS_DATE_LANGUAGE=ENGLISH') 
                                              in ('6','7') then 
               0
              when date'2018-08-30'+level in (date'2018-09-25',date'2018-09-26') then
               0
              else
               level
              end) as date_field_two, 
 sum(cast(case when to_char(date'2018-08-30'+level,'D','NLS_DATE_LANGUAGE=ENGLISH')  
                                               in ('6','7') then 
                0
               when date'2018-08-30'+level in (date'2018-09-25',date'2018-09-26') then
                0 
               else
                1
               end as int)) over (order by level) as next_day
      from dual
    connect by level <= 20*2 
  )    
 where next_day = 20;

 DATE_FIELD_TWO
-----------------
   01.10.2018

它会在下一天进行迭代,在这种情况下,除非这个假期与周末重合。

答案 1 :(得分:0)

如果使用PL / SQL函数,则可以将工作日定义为任意日期

在这里有一个简单的原型-没有任何假期-但可以使用相同的逻辑将其修改为目的。

create or replace function add_business_days (from_date IN date, bd IN integer) return date as 
  fd date := trunc(from_date,'iw'); 
  cnt int := (from_date-fd)+bd-1; 
  ww int := ceil(cnt/5); 
  wd int := mod(cnt,5); 
begin
  return from_date + (ww*7)+wd; 
end;
/

答案 2 :(得分:0)

我知道您已经有了答案,但值得的是,我们一直在处理这件事,事实证明这是一个很好的解决方案。

实际上,我们维护着一个单独的表,称为“工作日”,该表具有我们可以比较的所有可能的日期(当然,每个应用程序的定义都会有所不同-但是在任何情况下,它都不会“很大(按RDBMS标准)。有一个布尔标志,指示日期是工作日还是周末/假日,但更重要的是,有一个仅在工作日递增的索引值。该表如下所示:

enter image description here

这样做的好处是透明度和可伸缩性。如果您希望工作日中两个日期之间的差额:

select
  h.entry_date, h.invoice_date, wd2.workday_index - wd1.workday_index as delta
from
  sales_order_data h
  join util.work_days wd1 on h.sales_order_entry_dte = wd1.cal_date
  join util.work_days wd2 on h.invoice_dte = wd2.cal_date

如果您需要在表格中加上一个日期并加上20天(例如您最初的问题陈述):

select
  h.date_field_1, wd2.cal_date as date_field_1_plus_20
from
  my_table h
  join util.work_days wd1 on h.date_field_1 = wd1.cal_date
  join util.work_days wd2 on
    wd1.workday_index + 20 = wd2.workday_index and
    wd2.is_workday

(免责声明,这是在PostgreSQL中,这就是为什么要使用布尔值的原因。在Oracle中,我猜您需要将其更改为整数并说=1

此外,对于奖金问题,这还提供了两个不同的选项来定义“工作日”,一个向前滚动,另一个向后滚动(因此,workday_index和workday_index_back)。例如,如果您在星期六需要,而星期六不是工作日,则意味着您在星期五需要它。相反,如果要在周六交付某物,而周六不在工作日,则意味着它将在周一开放。处理非工作日的上下文各不相同,这种方法使您可以选择合适的工作日。

作为最终卖点,此选项使您也可以将假期定义为非工作日...,您可以执行此操作或不执行此操作;由你决定。该解决方案允许任何一种选择。从理论上讲,您只能在周末的工作日索引中再添加两列,这给了您两个选择。