在Oracle SQL中计算工作日,包括不带日历表的日期之间的假期 [英] Calculating working days including holidays between dates without a calendar table in oracle SQL

查看:109
本文介绍了在Oracle SQL中计算工作日,包括不带日历表的日期之间的假期的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

好的,因此,我已经对在sql中模拟excel的networkdays函数的可能性进行了大量的阅读,得出的结论是,到目前为止,最简单的解决方案是拥有一个将标记工作的日历表.天或非工作日.但是,由于无法控制的情况,我们无法获得如此豪华的产品,并且我们不太可能在不久的将来有任何机会.

Okay, so I've done quite a lot of reading on the possibility of emulating the networkdays function of excel in sql, and have come to the conclusion that by far the easiest solution is to have a calendar table which will flag working days or non working days. However, due to circumstances out of my control, we don't have access to such a luxury and it's unlikely that we will any time in the near future.

目前,我已经设法将确实可以解决的SQL效率低下的查询合并在一起,但要注意的是,它一次只适用于一条客户记录.

Currently I have managed to bodge together what is undoubtedly a horrible ineffecient query in SQL that does work - the catch is, it will only work for a single client record at a time.

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)是否可以使它适用于多个客户?任何指针表示感谢!

Basically, I'm wondering if a) I should stop wasting my time on this or b) is it possible to get this to work for multiple clients? Any pointers appreciated thanks!

进一步说明-我已经使用excel制定了时间表,但是如果我们可以在报告中进行操作,那将是理想的选择,因为相关报告是我们希望最终用户能够在没有任何进一步要求的情况下运行的工具操作.

Further clarification - I already work out timescales using excel, but it would be ideal if we could do it in the report as the report in question is something that we would like end users to be able to run without any further manipulation.

MarkBannister的答案虽然很慢,但是效果很好(尽管我期望它不是首选的解决方案,但我期望如此之多)-现在的挑战在于将其集成到现有报告中!

MarkBannister's answer works perfectly albeit slowly (though I had expected as much given it's not the preferred solution) - the challenge now lies in me integrating this into an existing report!

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 

推荐答案

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

There are a few ways to do this. Perhaps the simplest might be to create a CTE that produces a virtual calendar table, based on Oracle's connect by syntax, and then join it to the Assesments table, like so:

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子句中指定了所有日期(即最多27个) 2011年12月)也标记为非工作日.

This will produce a virtual table populated with dates from 01 January 2000 to 10 years after the current date, with all weekends marked as non-working days and all days specified in the second in clause (ie. up to 27 December 2011) also marked as non-working days.

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

The drawback of this method (or any method where the holiday dates are hardcoded into the query) is that each time new holiday dates are defined, every single query that uses this approach will have to have those dates added.

这篇关于在Oracle SQL中计算工作日,包括不带日历表的日期之间的假期的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆