Расчет рабочих дней, включая праздничные дни между датами без календарной таблицы в Oracle SQL - PullRequest
2 голосов
/ 17 января 2012

Хорошо, поэтому я довольно много читал о возможности эмуляции функции networkdays в Excel в sql и пришел к выводу, что на сегодняшний день самым простым решением является наличие таблицы календаря, которая будет отмечать работудни или нерабочие дни.Однако из-за обстоятельств, не зависящих от меня, у нас нет доступа к такой роскоши, и маловероятно, что мы сделаем это в ближайшее время.

В настоящее время мне удалось собрать воедино, что, несомненно, является ужасным неэффективным запросом в 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

По сути, мне интересно, а) я должен прекратить тратить свое время на это или б) можно ли заставить это работать для нескольких клиентов?Спасибо всем указателям!

Редактировать: Дополнительные разъяснения - я уже рассчитываю сроки, используя 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 ]

3 голосов
/ 17 января 2012

Есть несколько способов сделать это.Возможно, самым простым может быть создание 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

Это создаст виртуальныйтаблица, заполненная датами с 1 января 2000 года по 10 лет после текущей даты, причем все выходные отмечены как нерабочие дни, а все дни, указанные во втором пункте in (т.е. до 27 декабря 2011 года), также отмечены как нерабочиерабочих дней.

Недостаток этого метода (или любого метода, в котором даты праздников жестко закодированы в запросе) состоит в том, что каждый раз, когда определяются новые даты праздников, каждый запрос, использующий этот подход, должен иметь тедобавлены даты.

0 голосов
/ 17 января 2012

Если вы не можете использовать таблицу календаря в Oracle, возможно, вам лучше экспортировать в Excel.Грубая сила всегда работает.

Networkdays ()"возвращает количество полных рабочих дней между start_date и end_date. Рабочие дни исключают выходные и любые даты, указанные в праздничные дни."

Исключая выходные, кажется довольно простым.Каждый 7-дневный период будет содержать два выходных дня.Вам просто нужно позаботиться о оставшихся днях.

Праздники - это отдельная история.Вы должны либо хранить их, либо передавать в качестве аргумента.Если бы вы могли хранить их, вы бы сохранили их в таблице календаря, и ваша проблема была бы решена.Но вы не можете этого сделать.

Так что вы смотрите на передачу их в качестве аргумента.Сверху головы - а сегодня утром я еще не пил чай - я бы рассмотрел обычное табличное выражение или оболочку для хранимой процедуры.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...