计算工作日,包括oracle SQL中没有日历表的日期之间的假期

时间:2012-01-17 10:01:47

标签: sql oracle

好的,所以我已经完成了很多关于在sql中模拟excel的networkdays函数的可能性,并得出结论,到目前为止最简单的解决方案是有一个标记工作的日历表天或非工作日。然而,由于我无法控制的情况,我们无法获得这样的奢侈品,而且我们不太可能在不久的将来任何时候。

目前,我已经设法将在SQL中无疑是一个可怕的无效查询工作联系在一起 - 实际上,它只能用于单个客户端记录。

SELECT O_ASSESSMENTS.ASM_ID,
       O_ASSESSMENTS.ASM_START_DATE,
       O_ASSESSMENTS.ASM_END_DATE,
      sum(CASE 
              When TO_CHAR(O_ASSESSMENTS.ASM_START_DATE + rownum -1,'Day')
                   = 'Sunday   ' THEN 0
              When TO_CHAR(O_ASSESSMENTS.ASM_START_DATE + rownum -1,'Day')
                   = 'Saturday ' THEN 0
              WHEN O_ASSESSMENTS.ASM_START_DATE + rownum - 1
                   IN ('03-01-2000','21-04-2000','24-04-2000','01-05-2000','29-05-2000','28-08-2000','25-12-2000','26-12-2000','01-01-2001','13-04-2001','16-04-2001','07-05-2001','28-05-2001','27-08-2001','25-12-2001','26-12-2001','01-01-2002','29-03-2002','01-04-2002','06-04-2002','03-06-2002','04-06-2002','26-08-2002','25-12-2002','26-12-2002','01-01-2003','18-04-2003','21-04-2003','05-05-2003','26-05-2003','25-08-2003','25-12-2003','26-12-2003','01-01-2004','09-04-2004','12-04-2004','03-05-2004','31-05-2004','30-08-2004','25-12-2004','26-12-2004','27-12-2004','28-12-2004','01-01-2005','03-01-2005','25-03-2005','28-03-2005','02-05-2005','30-05-2005','29-08-2005','27-12-2005','28-12-2005','02-01-2006','14-04-2006','17-04-2006','01-05-2006','29-05-2006','28-08-2006','25-12-2006','26-12-2006','02-01-2007','06-04-2007','09-04-2007','07-05-2007','28-05-2007','27-08-2007','25-12-2007','26-12-2007','01-01-2008','21-03-2008','24-03-2008','05-05-2008','26-05-2008','25-08-2008','25-12-2008','26-12-2008','01-01-2009','10-04-2009','13-04-2009','04-05-2009','25-05-2009','31-08-2009','25-12-2009','28-12-2009','01-01-2010','02-04-2010','05-04-2010','03-05-2010','31-05-2010','30-08-2010','24-12-2010','27-12-2010','28-12-2010','31-12-2010','03-01-2011','22-04-2011','25-04-2011','29-04-2011','02-05-2011','30-05-2011','29-08-2011','26-12-2011','27-12-2011') 
                   THEN 0 
              ELSE 1 
          END)-1 AS Week_Day
From O_ASSESSMENTS,
     ALL_OBJECTS
WHERE O_ASSESSMENTS.ASM_QSA_ID  IN ('TYPE1')
  AND O_ASSESSMENTS.ASM_END_DATE >= '01/01/2012'
  AND O_ASSESSMENTS.ASM_ID = 'A00000'
  AND ROWNUM <= O_ASSESSMENTS.ASM_END_DATE-O_ASSESSMENTS.ASM_START_DATE+1
GROUP BY
      O_ASSESSMENTS.ASM_ID,
      O_ASSESSMENTS.ASM_START_DATE,
      O_ASSESSMENTS.ASM_END_DATE

基本上,我想知道是否a)我应该停止浪费我的时间或b)是否有可能让这个为多个客户工作?任何指针都表示赞赏!

编辑:进一步澄清 - 我已经使用excel计算出时间表,但如果我们能够在报告中做到这一点将是理想的,因为我们希望最终用户能够在没有任何进一步的情况下运行该报告操纵。

编辑:

MarkBannister的答案虽然缓慢(但我已经预料到它不是首选的解决方案)但效果很好 - 现在的挑战在于我将其整合到现有的报告中!

with
calendar_cte as (select
to_date('01-01-2000')+level-1 calendar_date,
case when to_char(to_date('01-01-2000')+level-1, 'day') in ('sunday   ','saturday ') then 0 when to_date('01-01-2000')+level-1 in ('03-01-2000','21-04-2000','24-04-2000','01-05-2000','29-05-2000','28-08-2000','25-12-2000','26-12-2000','01-01-2001','13-04-2001','16-04-2001','07-05-2001','28-05-2001','27-08-2001','25-12-2001','26-12-2001','01-01-2002','29-03-2002','01-04-2002','06-04-2002','03-06-2002','04-06-2002','26-08-2002','25-12-2002','26-12-2002','01-01-2003','18-04-2003','21-04-2003','05-05-2003','26-05-2003','25-08-2003','25-12-2003','26-12-2003','01-01-2004','09-04-2004','12-04-2004','03-05-2004','31-05-2004','30-08-2004','25-12-2004','26-12-2004','27-12-2004','28-12-2004','01-01-2005','03-01-2005','25-03-2005','28-03-2005','02-05-2005','30-05-2005','29-08-2005','27-12-2005','28-12-2005','02-01-2006','14-04-2006','17-04-2006','01-05-2006','29-05-2006','28-08-2006','25-12-2006','26-12-2006','02-01-2007','06-04-2007','09-04-2007','07-05-2007','28-05-2007','27-08-2007','25-12-2007','26-12-2007','01-01-2008','21-03-2008','24-03-2008','05-05-2008','26-05-2008','25-08-2008','25-12-2008','26-12-2008','01-01-2009','10-04-2009','13-04-2009','04-05-2009','25-05-2009','31-08-2009','25-12-2009','28-12-2009','01-01-2010','02-04-2010','05-04-2010','03-05-2010','31-05-2010','30-08-2010','24-12-2010','27-12-2010','28-12-2010','31-12-2010','03-01-2011','22-04-2011','25-04-2011','29-04-2011','02-05-2011','30-05-2011','29-08-2011','26-12-2011','27-12-2011','01-01-2012','02-01-2012') then 0 else 1 end working_day
from dual
connect by level <= 1825 + sysdate - to_date('01-01-2000') ) 
SELECT 
a.ASM_ID,
a.ASM_START_DATE,
a.ASM_END_DATE,
sum(c.working_day)-1 AS Week_Day 
From 
O_ASSESSMENTS a 
join calendar_cte c
on c.calendar_date between a.ASM_START_DATE and a.ASM_END_DATE 
WHERE a.ASM_QSA_ID  IN ('TYPE1')
and a.ASM_END_DATE >= '01/01/2012'
GROUP BY      
a.ASM_ID,
a.ASM_START_DATE,
a.ASM_END_DATE 

2 个答案:

答案 0 :(得分:3)

有几种方法可以做到这一点。最简单的可能是创建一个CTE,根据Oracle的connect by语法生成虚拟日历表,然后将其加入Assesments表,如下所示:

with calendar_cte as (
select to_date('01-01-2000')+level-1 calendar_date,
       case when to_char(to_date('01-01-2000')+level-1, 'Day') 
                in ('Sunday   ','Saturday ') then 0
            when to_date('01-01-2000')+level-1
                in ('03-01-2000','21-04-2000','24-04-2000','01-05-2000','29-05-2000','28-08-2000','25-12-2000','26-12-2000','01-01-2001','13-04-2001','16-04-2001','07-05-2001','28-05-2001','27-08-2001','25-12-2001','26-12-2001','01-01-2002','29-03-2002','01-04-2002','06-04-2002','03-06-2002','04-06-2002','26-08-2002','25-12-2002','26-12-2002','01-01-2003','18-04-2003','21-04-2003','05-05-2003','26-05-2003','25-08-2003','25-12-2003','26-12-2003','01-01-2004','09-04-2004','12-04-2004','03-05-2004','31-05-2004','30-08-2004','25-12-2004','26-12-2004','27-12-2004','28-12-2004','01-01-2005','03-01-2005','25-03-2005','28-03-2005','02-05-2005','30-05-2005','29-08-2005','27-12-2005','28-12-2005','02-01-2006','14-04-2006','17-04-2006','01-05-2006','29-05-2006','28-08-2006','25-12-2006','26-12-2006','02-01-2007','06-04-2007','09-04-2007','07-05-2007','28-05-2007','27-08-2007','25-12-2007','26-12-2007','01-01-2008','21-03-2008','24-03-2008','05-05-2008','26-05-2008','25-08-2008','25-12-2008','26-12-2008','01-01-2009','10-04-2009','13-04-2009','04-05-2009','25-05-2009','31-08-2009','25-12-2009','28-12-2009','01-01-2010','02-04-2010','05-04-2010','03-05-2010','31-05-2010','30-08-2010','24-12-2010','27-12-2010','28-12-2010','31-12-2010','03-01-2011','22-04-2011','25-04-2011','29-04-2011','02-05-2011','30-05-2011','29-08-2011','26-12-2011','27-12-2011') 
                then 0 
            else 1
       end working_day
from dual
connect by level <= 36525 + sysdate - to_date('01-01-2000') )
SELECT a.ASM_ID,
       a.ASM_START_DATE,
       a.ASM_END_DATE,
       sum(c.working_day) AS Week_Day
From O_ASSESSMENTS a
join calendar_cte c 
  on c.calendar_date between a.ASM_START_DATE and a.ASM_END_DATE
WHERE a.ASM_QSA_ID  IN ('TYPE1') and 
      a.ASM_END_DATE >= '01/01/2012' -- and a.ASM_ID = 'A00000'
GROUP BY
      a.ASM_ID,
      a.ASM_START_DATE,
      a.ASM_END_DATE

这将生成一个虚拟表,其中填充的日期为2000年1月1日至当前日期之后的10年,所有周末都标记为非工作日以及第二个in子句中指定的所有日期(即向上)至2011年12月27日)也标记为非工作日。

此方法(或将假日日期硬编码到查询中的任何方法)的缺点是,每次定义新假日日期时,使用此方法的每个查询都必须添加这些日期。

答案 1 :(得分:0)

如果您无法在Oracle中使用日历表,则最好导出到Excel。蛮力总是有效。

Networkdays()“返回start_date和end_date之间的整个工作日数。工作日不包括周末和假日中确定的任何日期。”

排除周末似乎相当简单。每7天一段时间将包含两个周末。你只需要关心剩下的日子。

假期是另一回事。您必须存储它们或将它们作为参数传递。如果你可以存储它们,你将它们存储在日历表中,你的问题就会结束。但你不能这样做。

所以你正在考虑将它们作为一个论点。在我的头顶 - 我今天早上还没有喝茶 - 我会考虑一个常见的表格表达式或存储过程的包装。