前几个月别人的项目上面要用这个,当时想法有点陷入误区,一下子没搞出来,后来交研发去用代码实现了。
最近自己项目也遇到这个需求了,换了个思路,没想到成功了,试了试基本没大问题,但又不太敢保证应付所有情况,比如跨年我还没试......
且算是集思广益吧。
环境:SQL Server 2008
准备工作:
建一个库,用来存放节假日(datetime型),参考下面这张图的:
代码:
会有2个存储过程:入口是test2,test1当作函数用被test2调用
test1:
1 ALTER PROCEDURE [dbo].[test1] 2 @sdate datetime,@sdd int , @sdd3 int output,@sdd33 datetime output 3 AS 4 BEGIN 5 SET NOCOUNT ON; 6 declare @neardate datetime; 7 declare @tmpdate datetime; 8 declare @mystr varchar(600); 9 declare @nint int; 10 declare @gint int; 11 12 13 --循环计数开始值 14 15 set @nint=1; 16 17 --选最近的一个休息日 18 select top 1 @neardate=thedate from thedate where thedate > @sdate order by thedate asc; 19 20 set @tmpdate=@neardate; 21 22 set @mystr=''; 23 24 --计算和最近一个休息日之间的差 25 26 set @gint=DATEDIFF(DAY,@sdate,@neardate); 27 28 --select @gint; 29 30 --30天连休应该不存在的....所以这边取个30,也可以根据输入项再作判断,但有点烦就算了 31 --取出一连串的休息日形成一个以逗号间隔的文本用于后面 的查找 32 33 select top 30 @mystr=@mystr+','+ CONVERT(varchar,thedate,23) from thedate where thedate > @sdate order by thedate asc; 34 35 36 --看到最近一次休息日的天数是否大于需要增加的天数,大于的话就直接增加了,也不用再跳过什么休息日了 37 38 if(@gint >= @sdd ) 39 begin 40 --select @gint as t1,@sdd as t11; 41 set @sdd3 = @gint; 42 set @sdd33 = DATEADD(day,@sdd-1,@sdate); --这里-1代表开始日期也算1天 43 end 44 else 45 --需要跳过休息日,则从最近一次的非工作日算起,逐步+1天,找出不是休息的那一天 46 begin 47 --select @gint as t1else,@sdd as t11else; 48 while(@nint<20) 49 begin 50 if(CHARINDEX(CONVERT(varchar,@tmpdate,23),@mystr)>0) --发现连休就继续 51 begin 52 set @tmpdate=DATEADD(day,1,@tmpdate); 53 set @nint = @nint+1; 54 end 55 else --发现工作日,则中断 56 begin 57 break ; 58 end 59 end 60 set @sdd3 = @gint; 61 set @sdd33 = @tmpdate; 62 63 end 64 --select @tmpdate as enddate,@gint as gonum ; 65 --最终返回两个值,下一次计算的开始日期,和到这次开始日期间隔的工作日天数 66 END
--这里偷点懒,直接复制了我自己的代码,所以是“ALTER PROCEDURE”,直接用用不了,要改下。
test2:
1 ALTER PROCEDURE [dbo].[test2] 2 @sdate datetime,@sdd int,@sdd3 datetime output 3 AS 4 BEGIN 5 6 declare @tmpint int; 7 declare @sd int; 8 9 declare @tmpdate datetime; 10 11 declare @stmpint int; 12 declare @stmpdate datetime; 13 14 SET NOCOUNT ON; 15 16 17 set @sd = @sdd; 18 19 set @stmpint = @sdd; 20 set @stmpdate = @sdate; 21 22 while(@sd>0) 23 begin 24 --select @stmpdate as t2,@stmpint as t22; 25 26 exec test_tri.dbo.test1 @stmpdate,@stmpint,@tmpint output,@tmpdate output; 27 28 set @sd = @sd-@tmpint; 29 set @stmpint = @sd; 30 set @stmpdate = @tmpdate; 31 end 32 33 set @sdd3=@tmpdate; 34 35 END
调用效果:
总结:
简单的来说就把存储过程test1当函数来用,将起始日期和增加的天数给test1,test1判断增加的天数内是否会遇到休息日,遇到了就按顺序去找接下来的工作日并给出起始日期到最近休息日期间的天数(遇到这段时间内没有休息日的就直接加上天数返回结果了)。比如起始日期是周一的日期,增加的天数是7,正常情况返回的日期就是下次的周一的日期,返回的天数就是5;
test2这个存储过程作为入口,调用的时候将起始日期和增加的天数给test2,test2建一个循环去调用test1,并不断的减去test1返回的天数(剩下的工作日),并以test1返回的日期以及天数作为参数再次去调用test1,以实现最终的目的。
这篇写的比较仓促,里面的参数写的有点乱,大家将就着看吧。我本身sqlserver不是太熟,不知道存储过程能不能递归,然后本来是想用函数实现test1的功能,实在不太会用,就写了2个存储过程......如果还能改进,望高手告之