在SQL / ORACLE中,我需要在昨天(今天 - 1)之前输出14个非周末天(不包括SAT / SUN)的日期。
例如,如果今天是05-MAY-16,我需要:
04-MAY-16 =昨天
14-APR-16 = 14个非周末回来
以下帮助我找到昨天的日期(不包括周末作为日期):
SELECT decode(to_char(sysdate,'dy'),'sun',sysdate-2,'tue',sysdate-3,sysdate-1) from dual
非常感谢任何帮助!
答案 0 :(得分:4)
如果你真的只想跳过周末并且根本不考虑其他假期,你可以使用递归CTE,锚分支从昨天开始,递归分支倒数而忽略周末几天,直到你到达足够远的地方。
您可以使用以下内容生成日期:
with r (the_date, days_ago, work_days_ago) as (
select trunc(sysdate) - 1, 0, 0
from dual
union all
select r.the_date - 1, days_ago + 1,
work_days_ago + case
when to_char(r.the_date - 1, 'DY', 'NLS_DATE_LANGUAGE=ENGLISH')
in ('SAT','SUN') then 0 else 1 end
from r
where days_ago < 28
and work_days_ago < 14
)
select r.*, to_char(r.the_date, 'DY')
from r;
THE_DATE DAYS_AGO WORK_DAYS_AGO TO_CHAR(R.TH
--------- ---------- ------------- ------------
04-MAY-16 0 0 WED
03-MAY-16 1 1 TUE
02-MAY-16 2 2 MON
01-MAY-16 3 2 SUN
30-APR-16 4 2 SAT
29-APR-16 5 3 FRI
28-APR-16 6 4 THU
27-APR-16 7 5 WED
26-APR-16 8 6 TUE
25-APR-16 9 7 MON
24-APR-16 10 7 SUN
23-APR-16 11 7 SAT
22-APR-16 12 8 FRI
21-APR-16 13 9 THU
20-APR-16 14 10 WED
19-APR-16 15 11 TUE
18-APR-16 16 12 MON
17-APR-16 17 12 SUN
16-APR-16 18 12 SAT
15-APR-16 19 13 FRI
14-APR-16 20 14 THU
获得你想要的那个:
...
select max(the_date)
from r
where work_days_ago = 14;
MAX(THE_D
---------
14-APR-16
在递归分支中,days_ago < 28
只是为递归提供静态停止条件;它永远不会达到,但它需要足够高,所以你可以肯定。然后work_days_ago < 14
阻止它看起来比实际需要更远。你不能只检查一下,因为你一到周末就会得到一个循环,因为这个数字不会增加。
在外部查询中,我使用了max,以防你在星期二运行它;然后递归将得到三天,work_days_ago
为14,表示星期一,星期日和星期六;而你只想要最新的那些。 (或者,而不是max()
,再次明确排除周末日。)
答案 1 :(得分:2)
如果问题仅针对工作日和周末,则无需复杂的解决方案。像下面的代码一样简单就足够了。
OP没有回复我的澄清请求,所以我需要做出一个假设。下面的代码解释了如下要求:
从今天开始,返回日历并向后计数,仅限工作日。最近的工作日获得计数0,下一个工作日1,我们返回计数= 14的工作日。(但是,代码没有通过计数得到答案;根据描述的要求,它使用一个通过案例进行简单的算术计算。)
如果要求不同,可以以同样的精神修改解决方案;无论要求如何,解决方案都应该同样简单。
select trunc(sysdate) - case to_char(sysdate, 'DY') when 'SAT' then 19
when 'SUN' then 20
else 21 end
from ... (etc.)
(根据Alex在下面评论中的观察结果进行了修正 - 感谢Alex!)
答案 2 :(得分:0)
工作日是一个难题。这是因为假期在每个国家都有所不同,有时在国内。假期可以像复活节一样落在不同的日子,可以在不同的日子观察。
您需要一个自定义程序包来计算这个以及一个列出每年假期和周末的表格。 (假设你周末没有工作)
您是在针对某个国家/地区的某个地点或多个地点进行此计算吗?
像
这样的东西CREATE TABLE HOLIDAYS as
( ID NUMBER(9), --primary key
HOLIDAY_ID NUMBER(9),
FOR_YEAR NUMBER(9), -- a constraint FOR_YEAR > 1 and < 9999
HOLIDAY_NAME VARCHAR2(200),
HOLIDAY_LENGTH NUMBER(9) DEFAULT 1,
LOCATION_ID NUMBER(9),
HOLIDAY_DATE DATE);
- 在FOR_YEAR,LOCATION_ID,HOLIDAY_ID上添加唯一键 - 每年每个假期和周末都有 以及另一个开展业务的地点表
在一个包中,你的计算将是
FUNCTION GET_BUSINESS_DAYS(start_date_in IN DATE, end_date_in DATE)
RETURN NUMBER
IS
v_days NUMBER(9);
v_holidays NUMBER(9);
BEGIN
--check that start_date_in and end_date_in are valid dates
--check that end_date_in is after start_date_in
--assumes holidays are only one day which is wrong in some cultures!
SELECT start_date_in - end_date_in
INTO v_days
FROM DUAL;
SELECT SUM(HOLIDAY_LENGTH)
INTO v_holidays
FROM HOLIDAYS
WHERE HOLIDAY_DATE BETWEEN start_date_in and end_date_in;
v_days := v_days - v_holidays;
--check that is this a positive value
RETURN v_days;
END GET_BUSINESS_DAYS;
有许多边缘情况需要检查,但这应该指向正确的
答案 3 :(得分:0)
试试这个。这是一个函数调用,只需要两个值(尽管有三个值可用于覆盖)。参数是日期(您要开始的日期),天数(计算的天数,在您的情况下为-14
function AddDays(day in date,days in number,WeekMask in char)
return date
is
begin
declare
NumWorkDays number := 0; /* Cumultive total for work days */
WorkDaysPerWeek number := 0;
FullWeeks number := 0; /* the number of 7-day weeks encompased by
the specified number of work days. To
wit, trunc(days/WorkDaysPerWeek) */
LastPartial number := 0; /* number of days in the trailing
partial week. */
CurDow number; /* Current Day of Week for Partial use */
WMask char(7) := 'YYYYYYY'; /* local copy of WeekMask with default
value - No weekends */
direct number := 1;
begin
/* copy Weekmask to local variable if it is defined, otherwise
implicitly use default mask */
direct := sign(days);
if direct = 0 then direct := 1; end if;
if WeekMask is not null then WMask := WeekMask; end if;
for CurDow in 1..7 loop /* go through mask counting work days */
if substr(WMask,CurDow,1) = 'Y' then /* if a weekday, count it*/
WorkDaysPerWeek := WorkDaysPerWeek + 1;
end if;
end loop;
/* determine number of Full weeks (7-day calendar) */
FullWeeks := trunc(days/WorkDaysPerWeek);
/* if the number of days is an integer number of Work Weeks
then decrement the number of Full Weeks so that the
iterative process will operate on the last week. This is
done so that weekend days are not used as ending days */
if FullWeeks != 0 and mod(days,WorkDaysPerWeek) = 0 then
FullWeeks := FullWeeks - direct;
end if;
/* Compute number of working days found so far */
NumWorkDays := FullWeeks*WorkDaysPerWeek;
/* Now go day by day through the last days of the period */
CurDow := to_number(to_char(day+FullWeeks*7,'D'));
NumWorkDays := NumWorkDays - direct;
while NumWorkDays != days loop
if substr(WMask,CurDow,1) = 'Y' then
NumWorkDays := NumWorkDays + direct;
end if;
If NumWorkDays = days then exit; end if;
LastPartial := LastPartial + direct;
CurDow := mod(CurDow+direct+6,7)+1;
end loop;
dout := day+FullWeeks*7+LastPartial; /* end date is number
weeks * 7 plus
number of additional days*/
return dout;
en
d;
end;