Нужно найти следующий и предыдущий рабочий день в оракуле - PullRequest
2 голосов
/ 13 января 2011

Мой запрос выглядит примерно так:

select 1 from dual where :p1_task_date in (sysdate,sysdate+1,sysdate-1) and :p1_task_id is not null

Это работает нормально, но я хотел получить следующие / предыдущие рабочие дни (следующие / предыдущие дни недели) вместо sysdate + 1 и sysdate-1. Я пробовал что-то вроде:

select next_day(sysdate, to_char(sysdate+1,'DAY')) from dual`

но не могу продолжить это: (

Пожалуйста, помогите !!!!

Ответы [ 6 ]

8 голосов
/ 14 января 2011

@ Ответ Таммана будет работать, но я предпочитаю этот метод для удобства чтения:

select sysdate as current_date,
       case when to_char(sysdate,'D') in (1,6,7)
            then next_day(sysdate,'Monday')
            else sysdate+1 end as next_weekday,
       case when to_char(sysdate,'D') in (1,2,7)
            then next_day(sysdate-7,'Friday')
            else sysdate-1 end as prev_weekday
from dual

Как и все остальные заявили, это будет работать только для исключения выходных, а не праздничных дней.

5 голосов
/ 13 января 2011

Без учета праздников вы можете использовать день недели для выполнения простой математики по дате, используя функцию DECODE:

SELECT SYSDATE-DECODE(TO_CHAR(SYSDATE, 'D'), 2, 3, 1, 2, 1) AS WORK_DATE_BEFORE,
        TO_CHAR(SYSDATE-DECODE(TO_CHAR(SYSDATE, 'D'), 2, 3, 1, 2, 1), 'DAY') AS WORK_DAY_BEFORE,
        SYSDATE AS BASE_DATE,
        TO_CHAR(SYSDATE, 'DAY') AS BASE_DAY,
        SYSDATE+DECODE(TO_CHAR(SYSDATE, 'D'), 6, 3, 7, 2, 1) AS WORK_DATE_AFTER,
        TO_CHAR(SYSDATE+DECODE(TO_CHAR(SYSDATE, 'D'), 6, 3, 7, 2, 1), 'DAY') AS WORK_DAY_AFTER
FROM DUAL

Просто замените SYSDATE переменной, содержащей дату для проверки. DECODE использует день недели, чтобы определить, сколько дней добавить или вычесть из базовой даты.

2 голосов
/ 30 сентября 2013

Чтобы вычислить дату для дней недели независимо от настроек языка, вы можете использовать усечение до начало недели ISO , что всегда понедельник .

Предыдущий рабочий день:

(
  case 
    when (date_value - trunc(date_value,'IW')) in (5,6,0)
      then trunc(date_value-1,'IW') + 4 
    else date_value - 1
  end
) prev_working_day

следующий рабочий день:

(
  case 
    when (date_value - trunc(date_value,'IW')) in (4,5,6)
      then trunc(date_value+3,'IW')
    else date_value + 1
  end
) next_working_day

Ниже приведен полный пример кода.

Проверка SQL Fiddle

with date_set as (
  select
    (trunc(sysdate) - 7 + level) as date_value
  from dual
  connect by level <= 14
),
calculated_days as (
  select 
    date_value,
    (
      case 
        when (date_value - trunc(date_value,'IW')) in (5,6,0)
          then trunc(date_value-1,'IW') + 4 
        else date_value - 1
      end
    ) prev_working_day,
    (
      case 
        when (date_value - trunc(date_value,'IW')) in (4,5,6)
          then trunc(date_value+3,'IW')
        else date_value + 1
      end
    ) next_working_day
  from 
    date_set
)
select
  date_value,
  to_char(date_value,'DAY') date_week_day,
  prev_working_day, 
  to_char(prev_working_day,'DAY') prev_day_week_day,
  next_working_day, 
  to_char(next_working_day,'DAY') next_day_week_day
from calculated_days
1 голос
/ 13 января 2011

Я думаю, что лучший способ сделать это - использовать dbms_scheduler для создания расписания всех ваших рабочих дней. Таким образом, вы можете настроить его по мере необходимости, и ваш код никогда не должен меняться. Создав расписание, используйте функцию dbms_scheduler.evaluate_calendar_string для расчета следующей даты. Это будет сделано с понедельника по пятницу, но вы можете легко улучшить график, чтобы также убрать праздничные дни:

set serveroutput on 
DECLARE 
  lv_next_work_date DATE; 
BEGIN 
  dbms_scheduler.create_schedule(schedule_name=>'MY_WORKDAY_SCHEDULE', 
                                 repeat_interval=>'FREQ=DAILY;BYDAY=MON,TUE,WED,THU,FRI'); 
  dbms_scheduler.evaluate_calendar_string(start_date => trunc(sysdate), 
                                          calendar_string => 'MY_WORKDAY_SCHEDULE', 
                                          return_date_after => trunc(sysdate), 
                                          next_run_date => lv_next_work_date); 
  dbms_output.put_line(lv_next_work_date); 
END; 
/ 

Бонус - вы также можете использовать его для автоматического выполнения заданий в рабочие дни.

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

0 голосов
/ 22 августа 2013

, чтобы пропустить только выходные:

select 
       in_date,  
       case when next_day(in_date,'Monday')>next_day(in_date,'Friday') 
                then in_date+1 else next_day(in_date,'Monday') end next_w_day, 
       case when next_day(in_date-8,'Friday')<next_day(in_date-8,'Monday') 
                then in_date-1 else next_day(in_date-7,'Friday') end previous_w_day
from 
       (select trunc(sysdate)+rownum in_date from 
                (select * from all_objects where rownum<15))
                order by in_date
0 голосов
/ 04 марта 2013

Данная процедура позволяет получить рабочие дни, исключая праздничные и выходные дни:

create or replace procedure GetWorkDays(current_day in date default sysdate,
                                    next_date out date,
                                    prev_date out date) is
TYPE  HOLIDAY_TYPE IS VARRAY(17) OF varchar(5);
--List all holidays here
holidays HOLIDAY_TYPE := HOLIDAY_TYPE('01.01','02.01','03.01','04.01',
                                    '05.01','06.01','07.01','08.01',
                                    '23.02','08.03','01.05','02.05',
                                    '03.05','09.05','10.05','12.06',
                                    '04.11'); 
--Internal functions-------------------------------------------------
function IsHoliday(currentDay date) return number is
begin
for i in holidays.first..holidays.last
   loop
       if to_char(currentDay,'DD.MM') = holidays(i) then return 1;
       end if;
   end loop;
return 0;
end;

function GetNextWorkDay(currentDay date) return date is
tempDate Date;
begin
tempDate:=currentDay+1;
while IsHoliday(tempDate)=1 loop
   tempDate:=tempDate+1;
end loop;
if to_char(tempDate,'D') in (6,7) then
   tempDate:=next_day(tempDate,'Monday');
end if;
if IsHoliday(tempDate)=1 then return GetNextWorkDay(tempDate);
else return tempDate;
end if;
end;

function GetPrevWorkDay(currentDay date) return date is
tempDate Date;
begin
tempDate:=currentDay-1;
while IsHoliday(tempDate)=1 loop
   tempDate:=tempDate-1;
end loop;
if to_char(tempDate,'D') in (6,7) then
   tempDate:=next_day(tempDate-7,'Friday');
end if;
if IsHoliday(tempDate)=1 then return GetPrevWorkDay(tempDate);
else return tempDate;
end if;
end;
------------------------------------------------------------------
begin

next_date:=GetNextWorkDay(current_day);
prev_date:=GetPrevWorkDay(current_day);

end GetWorkDays;
...