我创建了一个SQL函数,它接受两个日期,然后查找它们之间的天数,不包括周末和假日。它可以工作,但问题是每行需要1.5秒才能运行。这是一个无赖,因为我们正在为可能长达数千行的报告运行此功能。
对于更多背景知识,cteTally表只是一个通用的计数表。 HLD1表是一个如下所示的表:
Calendar StrDate EndDate
2016Holidays 1/1/16 1/1/16
2016Holidays 5/30/16 5/30/16
2016Holidays 7/4/16 7/4/16
2016Holidays 9/5/16 9/5/16
2016Holidays 11/24/16 11/25/16
2016Holidays 12/26/16 12/26/16
基本上它的意图是,当客户打电话给洗衣机的维修请求时,我们希望跟踪响应时间,但仅计算我们计数中的工作日。因此,例如,如果客户在6月30日下午4:30打电话,并且技术人员在7月5日上午8:30进行维修,那么我们将计算周五7/1(非假期,非周末日期),不计算周六7月2日或周日7/3(周末日),不计算周一7/4(节假日),并计算星期二7/5,以获得过夜的准确计数。
它将返回的答案是2。
为了做到这一点,它会查看功能中的日期并说“当这些天符合这些条件时,添加或减去一天,以便开始或结束日期不是周末或假日。”# 34;
我大约98.36%肯定必须有一个更好/更简单的方法来做到这一点,而且我想要过度思考正在发生的事情,但我无法做到我的生活决定了我能以不同的方式做些什么,而且我现在已经开关3天了。
编辑(在我发布之前):我现在已经把它减少到19秒,因为我正在使用该功能返回14560行。但是,在我修改函数假期之前帐户,它能够在不到一秒的时间内返回所有行。我所做的改变是限制"假期"在开始和结束日期之间返回假期。但是,我认为" WHILE"循环仍然是一些问题。我还尝试创建一个临时表或一个cte表来存储假期,所以它只需要每行计算一次而不是每行计算4次,但它并没有显示出来在函数中是可能的。
我现在要继续努力,但我会非常乐意为所有人提供帮助。
编辑2:我把所有" SELECT @ variables"它们在同一个select语句中连续而不是具有单独的那些,并且将时间减少了9秒,持续了14000行。但是,如果可能的话,我仍然希望在2秒的屏障下,或者至少5秒的屏障。
这是函数的文本:
CREATE FUNCTION [dbo].[dateDiffHolidays] (
@startdaytime DATETIME,
@enddaytime DATETIME
)
RETURNS INT
AS
BEGIN
DECLARE @answer INT;
DECLARE @START Date;
DECLARE @END Date;
DECLARE @AddDays int;
SET @answer = 0
-- Strip Times
SELECT @START = dateadd(dd,0, datediff(dd,0,@StartDayTime))
SELECT @END = dateadd(dd,0, datediff(dd,0,@EndDayTime))
SELECT @AddDays = count(*) from (SELECT
dateadd(dd,ctetally.n-1,@START) date1
from
cteTally
where dateadd(dd,ctetally.n-1,@START) <= @END) s1 where s1.date1 in (select cast( DATEADD(day, t.N - 1, StrDate) as date) as ResultDate
from HLD1 s join cteTally t on t.N <= DATEDIFF(day, StrDate, EndDate) + 1)
or datepart(dw,s1.date1) in (1,7)
-- handle end conditions
DECLARE @firstWeekDayInRange datetime, @lastWeekDayInRange datetime;
SET @firstWeekDayInRange = @START
set @lastWeekDayInRange = @END
WHILE @firstWeekDayInRange in (select cast( DATEADD(day, t.N - 1, StrDate) as date) as ResultDate
from HLD1 s join cteTally t on t.N <= DATEDIFF(day, StrDate, EndDate) + 1)
or datepart(dw,@firstWeekDayInRange) in (1,7)
BEGIN
SELECT @firstWeekDayInRange =
CASE
WHEN @firstWeekDayInRange in (select cast( DATEADD(day, t.N - 1, StrDate) as date) from HLD1 s join cteTally t on t.N <= DATEDIFF(day, StrDate, EndDate) + 1)
or datepart(dw,@firstWeekDayInRange) in (1,7)
THEN dateadd(DAY,1,@firstWeekDayInRange)
ELSE @firstWeekDayInRange
END
END
WHILE @lastWeekDayInRange in (select cast( DATEADD(day, t.N - 1, StrDate) as date) as ResultDate
from HLD1 s join cteTally t on t.N <= DATEDIFF(day, StrDate, EndDate) + 1)
or datepart(dw,@lastWeekDayInRange) in (1,7)
BEGIN
SELECT @lastWeekDayInRange =
CASE
WHEN @lastWeekDayInRange in (select cast( DATEADD(day, t.N - 1, StrDate) as date) from HLD1 s join cteTally t on t.N <= DATEDIFF(day, StrDate, EndDate) + 1)
or datepart(dw,@lastWeekDayInRange) in (1,7)
THEN dateadd(DAY,-1,@lastWeekDayInRange)
ELSE @lastWeekDayInRange
END
END
-- add one day to answer (to count Friday) if enddate was on a weekend
SELECT @answer = @answer +
CASE
-- triggered if start and end date are on same weekend
WHEN dateDiff(DAY,@firstWeekDayInRange,@lastWeekDayInRange) < 0 THEN (@answer * -1)
-- otherwise count the days and substract 2 days per weekend in between dates
ELSE (DateDiff(DAY, @firstWeekDayInRange, @lastWeekDayInRange) - @AddDays)
END
RETURN @answer
END
GO
答案 0 :(得分:1)
也许这会有所帮助。
Declare @HLD1 table (Calendar varchar(50),StrDate Date,EndDate Date)
Insert Into @HLD1 values
('2016Holidays','1/1/16','1/1/16'),
('2016Holidays','5/30/16','5/30/16'),
('2016Holidays','7/4/16','7/4/16'),
('2016Holidays','9/5/16','9/5/16'),
('2016Holidays','11/24/16','11/25/16'),
('2016Holidays','12/26/16','12/26/16')
Declare @Date1 Date = '2016-06-30 16:30:00'
Declare @Date2 Date = '2016-07-05 08:30:00'
Select DateDiff(DD,@Date1,@Date2)-sum(Excl)
From (
Select RetVal,Excl=max(Excl)
From (
Select *,Excl=IIF(DatePart(WEEKDAY,RetVal) in (7,1),1,0) From [dbo].[udf-Create-Range-Date](@Date1,@Date2,'DD',1)
Union All
Select RetVal=StrDate,Excl=1 From @HLD1 Where StrDate Between @Date1 and @Date2
) A
Group By RetVal
) A
返回
2
你仍然可以使用你的计数表,但我更喜欢我的UDF来创建 动态日期范围
CREATE FUNCTION [dbo].[udf-Create-Range-Date] (@DateFrom datetime,@DateTo datetime,@DatePart varchar(10),@Incr int)
Returns
@ReturnVal Table (RetVal datetime)
As
Begin
With DateTable As (
Select DateFrom = @DateFrom
Union All
Select Case @DatePart
When 'YY' then DateAdd(YY, @Incr, df.dateFrom)
When 'QQ' then DateAdd(QQ, @Incr, df.dateFrom)
When 'MM' then DateAdd(MM, @Incr, df.dateFrom)
When 'WK' then DateAdd(WK, @Incr, df.dateFrom)
When 'DD' then DateAdd(DD, @Incr, df.dateFrom)
When 'HH' then DateAdd(HH, @Incr, df.dateFrom)
When 'MI' then DateAdd(MI, @Incr, df.dateFrom)
When 'SS' then DateAdd(SS, @Incr, df.dateFrom)
End
From DateTable DF
Where DF.DateFrom < @DateTo
)
Insert into @ReturnVal(RetVal) Select DateFrom From DateTable option (maxrecursion 32767)
Return
End
-- Syntax Select * from [dbo].[udf-Create-Range-Date]('2016-10-01','2020-10-01','YY',1)
-- Syntax Select * from [dbo].[udf-Create-Range-Date]('2016-10-01','2020-10-01','DD',1)
-- Syntax Select * from [dbo].[udf-Create-Range-Date]('2016-10-01','2016-10-31','MI',15)
-- Syntax Select * from [dbo].[udf-Create-Range-Date]('2016-10-01','2016-10-02','SS',1)
答案 1 :(得分:0)
如果您可以将其转换为值为UDF的内联表,则查询可能会更快。虽然查看了您的UDF代码,但可能不容易/不容易。