如何计算两个日期之间的营业时间?例如,我们有两个日期; 01/01/2010 15:00和04/01/2010 12:00 我们在工作日的工作时间09:00至17:00 如何使用sql计算工作时间?计算两个日期之间的营业时间
回答
巴兰的答案固定,并修改所有的变量(周末,节假日等) 2005年SQL
SQL 2008及以上:
-- =============================================
-- Author: Baran Kaynak (modified by Kodak 2012-04-18)
-- Create date: 14.03.2011
-- Description: 09:30 ile 17:30 arasındaki iş saatlerini hafta sonlarını almayarak toplar.
-- =============================================
CREATE FUNCTION [dbo].[WorkTime]
(
@StartDate DATETIME,
@FinishDate DATETIME
)
RETURNS BIGINT
AS
BEGIN
DECLARE @Temp BIGINT
SET @Temp=0
DECLARE @FirstDay DATE
SET @FirstDay = CONVERT(DATE, @StartDate, 112)
DECLARE @LastDay DATE
SET @LastDay = CONVERT(DATE, @FinishDate, 112)
DECLARE @StartTime TIME
SET @StartTime = CONVERT(TIME, @StartDate)
DECLARE @FinishTime TIME
SET @FinishTime = CONVERT(TIME, @FinishDate)
DECLARE @WorkStart TIME
SET @WorkStart = '09:00'
DECLARE @WorkFinish TIME
SET @WorkFinish = '17:00'
DECLARE @DailyWorkTime BIGINT
SET @DailyWorkTime = DATEDIFF(MINUTE, @WorkStart, @WorkFinish)
IF (@StartTime<@WorkStart)
BEGIN
SET @StartTime = @WorkStart
END
IF (@FinishTime>@WorkFinish)
BEGIN
SET @[email protected]
END
IF (@FinishTime<@WorkStart)
BEGIN
SET @[email protected]
END
IF (@StartTime>@WorkFinish)
BEGIN
SET @StartTime = @WorkFinish
END
DECLARE @CurrentDate DATE
SET @CurrentDate = @FirstDay
DECLARE @LastDate DATE
SET @LastDate = @LastDay
WHILE(@CurrentDate<[email protected])
BEGIN
IF (DATEPART(dw, @CurrentDate)!=1 AND DATEPART(dw, @CurrentDate)!=7)
BEGIN
IF (@[email protected]) AND (@[email protected])
BEGIN
SET @Temp = @Temp + @DailyWorkTime
END
--IF it starts at startdate and it finishes not this date find diff between work finish and start as minutes
ELSE IF (@[email protected]) AND (@[email protected])
BEGIN
SET @Temp = @Temp + DATEDIFF(MINUTE, @StartTime, @WorkFinish)
END
ELSE IF (@[email protected]) AND (@[email protected])
BEGIN
SET @Temp = @Temp + DATEDIFF(MINUTE, @WorkStart, @FinishTime)
END
--IF it starts and finishes in the same date
ELSE IF (@[email protected]) AND (@[email protected])
BEGIN
SET @Temp = DATEDIFF(MINUTE, @StartTime, @FinishTime)
END
END
SET @CurrentDate = DATEADD(day, 1, @CurrentDate)
END
-- Return the result of the function
IF @Temp<0
BEGIN
SET @Temp=0
END
RETURN @Temp
END
SQL 2005和下面:
-- =============================================
-- Author: Baran Kaynak (modified by Kodak 2012-04-18)
-- Create date: 14.03.2011
-- Description: 09:30 ile 17:30 arasındaki iş saatlerini hafta sonlarını almayarak toplar.
-- =============================================
CREATE FUNCTION [dbo].[WorkTime]
(
@StartDate DATETIME,
@FinishDate DATETIME
)
RETURNS BIGINT
AS
BEGIN
DECLARE @Temp BIGINT
SET @Temp=0
DECLARE @FirstDay DATETIME
SET @FirstDay = DATEADD(dd, 0, DATEDIFF(dd, 0, @StartDate))
DECLARE @LastDay DATETIME
SET @LastDay = DATEADD(dd, 0, DATEDIFF(dd, 0, @FinishDate))
DECLARE @StartTime DATETIME
SET @StartTime = @StartDate - DATEADD(dd, DATEDIFF(dd, 0, @StartDate), 0)
DECLARE @FinishTime DATETIME
SET @FinishTime = @FinishDate - DATEADD(dd, DATEDIFF(dd, 0, @FinishDate), 0)
DECLARE @WorkStart DATETIME
SET @WorkStart = CONVERT(DATETIME, '09:00', 8)
DECLARE @WorkFinish DATETIME
SET @WorkFinish = CONVERT(DATETIME, '17:00', 8)
DECLARE @DailyWorkTime BIGINT
SET @DailyWorkTime = DATEDIFF(MINUTE, @WorkStart, @WorkFinish)
IF (@StartTime<@WorkStart)
BEGIN
SET @StartTime = @WorkStart
END
IF (@FinishTime>@WorkFinish)
BEGIN
SET @[email protected]
END
IF (@FinishTime<@WorkStart)
BEGIN
SET @F[email protected]
END
IF (@StartTime>@WorkFinish)
BEGIN
SET @StartTime = @WorkFinish
END
DECLARE @CurrentDate DATETIME
SET @CurrentDate = @FirstDay
DECLARE @LastDate DATETIME
SET @LastDate = @LastDay
WHILE(@CurrentDate<[email protected])
BEGIN
IF (DATEPART(dw, @CurrentDate)!=1 AND DATEPART(dw, @CurrentDate)!=7)
BEGIN
IF (@[email protected]) AND (@[email protected])
BEGIN
SET @Temp = @Temp + @DailyWorkTime
END
--IF it starts at startdate and it finishes not this date find diff between work finish and start as minutes
ELSE IF (@[email protected]) AND (@[email protected])
BEGIN
SET @Temp = @Temp + DATEDIFF(MINUTE, @StartTime, @WorkFinish)
END
ELSE IF (@[email protected]) AND (@[email protected])
BEGIN
SET @Temp = @Temp + DATEDIFF(MINUTE, @WorkStart, @FinishTime)
END
--IF it starts and finishes in the same date
ELSE IF (@[email protected]) AND (@[email protected])
BEGIN
SET @Temp = DATEDIFF(MINUTE, @StartTime, @FinishTime)
END
END
SET @CurrentDate = DATEADD(day, 1, @CurrentDate)
END
-- Return the result of the function
IF @Temp<0
BEGIN
SET @Temp=0
END
RETURN @Temp
END
帮助很多!谢谢 – 2013-09-24 08:04:59
我在这里有一个问题,@Temp返回仅差几分钟? – Somebody 2014-02-03 16:38:12
如果你知道1小时= 60分钟等,你可以很容易地转换它: ) – 2015-06-17 09:15:44
DECLARE @StartDate DATETIME
DECLARE @EndDate DATETIME
DECLARE @WORKINGHOURS INT
DECLARE @Days INT
SET @StartDate = '2010/01/01'
SET @EndDate = '2010/04/01'
--number of working days
SELECT @Days =
(DATEDIFF(dd, @StartDate, @EndDate) + 1)
-(DATEDIFF(wk, @StartDate, @EndDate) * 2)
-(CASE WHEN DATENAME(dw, @StartDate) = 'Sunday' THEN 1 ELSE 0 END)
-(CASE WHEN DATENAME(dw, @EndDate) = 'Saturday' THEN 1 ELSE 0 END)
--8 hours a day
SET @WORKINGHOURS = @Days * 8
SELECT @WORKINGHOURS
谢谢,但我在开始和结束日期有几个小时。我们如何在这个计算中增加这个未来? – Baran 2011-03-11 18:48:57
@Baran:看看我的解决方案 – 2016-11-15 09:31:50
从@ Pavanred的另一种解决方案,事物从一个更基于数据的角度来:
与所有你想要的是要考虑的日期创建一个表。每一天,设置了许多的工作时间,像这样:
WorkingDate Hours Comment
=========== ===== ==================
1 Jan 2011 0 Saturday
2 Jan 2011 0 Sunday
3 Jan 2011 0 Public Holiday
4 Jan 2011 8 Normal working day
5 Jan 2011 8 Normal working day
-- and so on, for all the days you want to report on.
这将需要建立少量 - 可以自动预填充它周与周末,然后调整为公众假期,等等,如有必要。
但是,你在建立输了,你在轻松愉悦的查询:
SELECT
SUM(Hours)
FROM
working_days
WHERE
WorkingDate BETWEEN @StartDate AND @EndDate
...这可以算出来作为一个更简单的方法,如果你需要开始添加更复杂的规则什么定义了一个工作日,或者如果你的工作时间取决于一天等等。
它也使规则更容易“可编辑”,因为你不需要改变任何实际的代码来改变定义一个工作日,增加公众假期等。
我h AVE实际上这样做之前,考虑到工作时间是非常困难的,我觉得这个任务最好SQL之外完成
-- =============================================
-- Author: Baran Kaynak
-- Create date: 14.03.2011
-- Description: 09:30 ile 17:30 arasındaki iş saatlerini hafta sonlarını almayarak toplar.
-- =============================================
CREATE FUNCTION [dbo].[WorkTime]
(
@StartDate DATETIME,
@FinishDate DATETIME
)
RETURNS BIGINT
AS
BEGIN
DECLARE @Temp BIGINT
SET @Temp=0
DECLARE @FirstDay DATE
SET @FirstDay = CONVERT(DATE, @StartDate, 112)
DECLARE @LastDay DATE
SET @LastDay = CONVERT(DATE, @FinishDate, 112)
DECLARE @StartTime TIME
SET @StartTime = CONVERT(TIME, @StartDate)
DECLARE @FinishTime TIME
SET @FinishTime = CONVERT(TIME, @FinishDate)
DECLARE @WorkStart TIME
SET @WorkStart = '09:30'
DECLARE @WorkFinish TIME
SET @WorkFinish = '17:30'
IF (@StartTime<@WorkStart)
BEGIN
SET @StartTime = @WorkStart
END
IF (@FinishTime>@WorkFinish)
BEGIN
SET @[email protected]
END
DECLARE @CurrentDate DATE
SET @CurrentDate = CONVERT(DATE, @StartDate, 112)
DECLARE @LastDate DATE
SET @LastDate = CONVERT(DATE, @FinishDate, 112)
WHILE(@CurrentDate<[email protected])
BEGIN
IF (DATEPART(dw, @CurrentDate)!=1 AND DATEPART(dw, @CurrentDate)!=7)
BEGIN
IF (@[email protected]) AND (@[email protected])
BEGIN
SET @Temp = (@Temp + (9*60))
END
--IF it starts at startdate and it finishes not this date find diff between work finish and start as minutes
ELSE IF (@[email protected]) AND (@[email protected])
BEGIN
SET @Temp = @Temp + DATEDIFF(MINUTE, @StartTime, @WorkFinish)
END
ELSE IF (@[email protected]) AND (@[email protected])
BEGIN
SET @Temp = @Temp + DATEDIFF(MINUTE, @WorkStart, @FinishTime)
END
--IF it starts and finishes in the same date
ELSE IF (@[email protected]) AND (@[email protected])
BEGIN
SET @Temp = DATEDIFF(MINUTE, @StartDate, @FinishDate)
END
END
SET @CurrentDate = DATEADD(day, 1, @CurrentDate)
END
-- Return the result of the function
IF @Temp<0
BEGIN
SET @Temp=0
END
RETURN @Temp
END
GO
ALTER FUNCTION WorkTime_fn (@StartDate DATETIME, @FinishDate DATETIME)
RETURNS VARCHAR(9)
AS
BEGIN
DECLARE @Temp BIGINT
SET @Temp=0
DECLARE @FirstDay VARCHAR(9)
SET @FirstDay = CONVERT(VARCHAR(9),@StartDate, 112)
DECLARE @LastDay VARCHAR(9)
SET @LastDay = CONVERT(VARCHAR(9),@FinishDate, 112)
DECLARE @StartTime VARCHAR(9)
SET @StartTime = CONVERT(VARCHAR(9),@StartDate, 108)
DECLARE @FinishTime VARCHAR(9)
SET @FinishTime = CONVERT(VARCHAR(9),@FinishDate, 108)
DECLARE @WorkStart VARCHAR(9)
SET @WorkStart = '09:30:00'
DECLARE @WorkFinish VARCHAR(9)
SET @WorkFinish = '17:30:00'
IF (@StartTime<@WorkStart)
BEGIN
SET @StartTime = @WorkStart
END
IF (@FinishTime>@WorkFinish)
BEGIN
SET @[email protected]
END
DECLARE @CurrentDate VARCHAR(9)
SET @CurrentDate = CONVERT(VARCHAR(9),@StartDate, 112)
DECLARE @LastDate VARCHAR(9)
SET @LastDate = CONVERT(VARCHAR(9),@FinishDate, 112)
WHILE(@CurrentDate<[email protected])
BEGIN
IF (DATEPART(dw, @CurrentDate)!=1 AND DATEPART(dw, @CurrentDate)!=7)
BEGIN
IF (@[email protected]) AND (@[email protected])
BEGIN
SET @Temp = (@Temp + (8*60))
END
ELSE IF (@[email protected]) AND (@[email protected])
BEGIN
SET @Temp = @Temp + DATEDIFF(MINUTE, @StartTime, @WorkFinish)
END
ELSE IF (@[email protected]) AND (@[email protected])
BEGIN
SET @Temp = @Temp + DATEDIFF(MINUTE, @WorkStart, @FinishTime)
END
ELSE IF (@[email protected]) AND (@[email protected])
BEGIN
SET @Temp = DATEDIFF(MINUTE, @StartTime, @FinishTime)
END
END
SET @CurrentDate = CONVERT(VARCHAR(9),DATEADD(day, 1, @CurrentDate),112)
END
Return @TEMP
END
我知道这是帖子很老,但这里是我最近写的一个函数,用来计算任何两个事件之间的营业时间/分钟。它还考虑了必须在表格中定义的任何假期。
函数返回以分钟为单位的时间间隔 - 您可以除以60以获得所需的小时数。
这已经在SQL Server 2008上测试过了。希望它能帮助别人。
Create Function GetWorkingMin(@StartDate DateTime, @EndDate DateTime, @Country Varchar(2)) Returns Int
AS
Begin
Declare @WorkMin int = 0 -- Initialize counter
Declare @Reverse bit -- Flag to hold if direction is reverse
Declare @StartHour int = 9 -- Start of business hours (can be supplied as an argument if needed)
Declare @EndHour int = 17 -- End of business hours (can be supplied as an argument if needed)
Declare @Holidays Table (HDate DateTime) -- Table variable to hold holidayes
-- If dates are in reverse order, switch them and set flag
If @StartDate>@EndDate
Begin
Declare @TempDate [email protected]
Set @[email protected]
Set @[email protected]
Set @Reverse=1
End
Else Set @Reverse = 0
-- Get country holidays from table based on the country code (Feel free to remove this or modify as per your DB schema)
Insert Into @Holidays (HDate) Select HDate from HOLIDAY Where [email protected] and HDATE>=DateAdd(dd, DateDiff(dd,0,@StartDate), 0)
If DatePart(HH, @StartDate)<@StartHour Set @StartDate = DateAdd(hour, @StartHour, DateDiff(DAY, 0, @StartDate)) -- If Start time is less than start hour, set it to start hour
If DatePart(HH, @StartDate)>[email protected]+1 Set @StartDate = DateAdd(hour, @StartHour+24, DateDiff(DAY, 0, @StartDate)) -- If Start time is after end hour, set it to start hour of next day
If DatePart(HH, @EndDate)>[email protected]+1 Set @EndDate = DateAdd(hour, @EndHour, DateDiff(DAY, 0, @EndDate)) -- If End time is after end hour, set it to end hour
If DatePart(HH, @EndDate)<@StartHour Set @EndDate = DateAdd(hour, @EndHour-24, DateDiff(DAY, 0, @EndDate)) -- If End time is before start hour, set it to end hour of previous day
If @StartDate>@EndDate Return 0
-- If Start and End is on same day
If DateDiff(Day,@StartDate,@EndDate) <= 0
Begin
If Datepart(dw,@StartDate)>1 And DATEPART(dw,@StartDate)<7 -- If day is between sunday and saturday
If (Select Count(*) From @Holidays Where HDATE=DateAdd(dd, DateDiff(dd,0,@StartDate), 0)) = 0 -- If day is not a holiday
If @EndDate<@StartDate Return 0 Else Set @WorkMin=DATEDIFF(MI, @StartDate, @EndDate) -- Calculate difference
Else Return 0
Else Return 0
End
Else Begin
Declare @Partial int=1 -- Set partial day flag
While DateDiff(Day,@StartDate,@EndDate) > 0 -- While start and end days are different
Begin
If Datepart(dw,@StartDate)>1 And DATEPART(dw,@StartDate)<7 -- If this is a weekday
Begin
If (Select Count(*) From @Holidays Where HDATE=DateAdd(dd, DateDiff(dd,0,@StartDate), 0)) = 0 -- If this is not a holiday
Begin
If @Partial=1 -- If this is the first iteration, calculate partial time
Begin
Set @[email protected] + DATEDIFF(MI, @StartDate, DateAdd(hour, @EndHour, DateDiff(DAY, 0, @StartDate)))
Set @StartDate=DateAdd(hour, @StartHour+24, DateDiff(DAY, 0, @StartDate))
Set @Partial=0
End
Else Begin -- If this is a full day, add full minutes
Set @[email protected] + (@[email protected])*60
Set @StartDate = DATEADD(DD,1,@StartDate)
End
End
Else Set @StartDate = DATEADD(DD,1,@StartDate)
End
Else Set @StartDate = DATEADD(DD,1,@StartDate)
End
If Datepart(dw,@StartDate)>1 And DATEPART(dw,@StartDate)<7 -- If last day is a weekday
If (Select Count(*) From @Holidays Where HDATE=DateAdd(dd, DateDiff(dd,0,@StartDate), 0)) = 0 -- And it is not a holiday
If @Partial=0 Set @[email protected] + DATEDIFF(MI, @StartDate, @EndDate) Else Set @[email protected] + DATEDIFF(MI, DateAdd(hour, @StartHour, DateDiff(DAY, 0, @StartDate)), @EndDate)
End
If @Reverse=1 Set @[email protected]
Return @WorkMin
End
这很好用!谢谢! – buzzzzjay 2013-01-31 22:05:21
太棒了,谢谢 – Craig 2014-11-26 15:41:42
这个代码恐怕有一个小错误。如果您的StartDate属于周末或假期,则会在开始日期的1天内加入帐户。因此,如果您的开始时间是星期日下午1点,它将从星期一下午1点开始计算,而不是星期一的StartHour。若要更正此问题,请更改两个相同的行,这些行读取:“Else Set(at)StartDate = DATEADD(DD,1,(at)StartDate)”to this:“Else Set(at)StartDate = DATEADD(HOUR,(at)开始时间,CAST(CAST(DATEADD(DD,1,(at)StartDate)AS DATE)AS DATETIME))“ – 2017-10-14 00:09:59
思维,下面的函数工作的另一种方式正确,如果你的一周的第一天是星期一,否则你应该改变相关的线,包括(6,7)到本地周末的日子
create function fn_worktime(@Datetime1 DateTime,@Datetime2 DateTime)
Returns BigInt
as
Begin
Declare
@Date1 Date,
@Date2 Date,
@DateIndex Date,
@minutes int,
@lastDayMinutes int,
@StartTime int , --in minutes
@FinishTime int ,--in minutes
@WorkDayLong int --in minutes
Set @StartTime =8 * 60 + 30 -- 8:30
Set @FinishTime =17* 60 + 30 -- 17:30
Set @WorkDayLong [email protected] - @StartTime
Set @Date1 = Convert(Date,@DateTime1)
Set @Date2 = Convert(Date,@DateTime2)
Set @minutes=DateDiff(minute,@DateTime1,DateAdd(MINUTE,@FinishTime ,convert(DateTime,@Date1)))
if @minutes<0 OR DatePart(dw,@Date1) in (6,7) -- you can even check holdays here. '(6 Saturday,7 Sunday) according to SET DATEFIRST 1'
Set @minutes=0
Set @DateIndex=DateAdd(day,1,@Date1)
While @DateIndex<@Date2
Begin
if DatePart(dw,@DateIndex) not in (6,7) -- you can even check holdays here. '(6 Saturday,7 Sunday) according to SET DATEFIRST 1'
set @[email protected][email protected]
Set @DateIndex=DateAdd(day,1,@DateIndex)
End
if DatePart(dw,@DateIndex) not in (6,7) -- you can even check holdays here
Begin
set @lastDayMinutes=DateDiff(minute,DateAdd(MINUTE ,@StartTime ,convert(DateTime,@Date2)),@DateTime2)
if @lastDayMinutes>@WorkDayLong
set @[email protected]
if @Date1<>@Date2
set @[email protected][email protected]
Else
Set @[email protected][email protected]@WorkDayLong
End
return @minutes
End
这里是一个内联版本 开始/结束日期像2015-03-16 09:52:24。000 开始/结束时间(businesshours)喜欢07:00:00 这是庞大的,但在您的选择声明
我也将它发布在功能版本以及。
Case when <StartDate>= <EndDate> then 0
When Convert(date,<StartDate>) = Convert(date,<EndDate>) Then
IIF(DATEPART(Dw,<StartDate>) in(1,7)
or Convert(time,<StartDate>) > Convert(time,<EndTime>)
or Convert(time,<EndDate>) < Convert(time,<StartTime>),0,
DateDiff(S,IIF(Convert(time,<StartDate>) < Convert(time,<StartTime>),Convert(time,<StartTime>),Convert(time,<StartDate>))
,IIF(Convert(time,<EndDate>) > Convert(time,<EndTime>), Convert(time,<EndTime>), Convert(time,<EndDate>))))
when Convert(date,<StartDate>) <> Convert(date,<EndDate>) then
IIF(DATEPART(Dw,<StartDate>) in(1,7) or Convert(time,<StartDate>) > Convert(time,<EndTime>),0 ,DateDiff(S,IIF(Convert(time,<StartDate>) < Convert(time,<StartTime>),Convert(time,<StartTime>),Convert(time,<StartDate>)), Convert(time,<EndTime>)))
+ IIF(DATEPART(Dw,<EndDate>) in(1,7) or Convert(time,<EndDate>) < Convert(time,<StartTime>),0,DateDiff(S,Convert(time,<StartTime>),IIF(Convert(time,<EndDate>) > Convert(time,<EndTime>), Convert(time,<EndTime>), Convert(time,<EndDate>))))
else -333
end --as pday
+IIF(DatePart(wEEk,<StartDate>) = DatePart(wEEk,<EndDate>)
,0, (DateDiff(wk,dateadd(d,-datepart(dw,<StartDate>),dateadd(ww,1,<StartDate>)),DATEADD(wk, DATEDIFF(wk, 6, <EndDate>), 6)-1) * 5)) * Datediff(S, Convert(time,<StartTime>),Convert(time,<EndTime>)) --Fullweek_days
+Case When Convert(date,<StartDate>) = Convert(date,<EndDate>) then 0
When DatePart(wEEk,<StartDate>) <> DatePart(wEEk,<EndDate>) then
IIF(datepart(dw,<StartDate>) = 7,0,DateDIFF(DAY,<StartDate>+1,dateadd(d,-datepart(dw,<StartDate>),dateadd(ww,1,<StartDate>)))) -- beginFulldays
+IIF(datepart(dw,<EndDate>) = 1,0,DateDIFF(DAY,DATEADD(wk, DATEDIFF(wk, 6, <EndDate>), 6),<EndDate> -1)) --Endfulldays
When DatePart(wEEk,<StartDate>) = DatePart(wEEk,<EndDate>) then
DateDiff(DAY,<StartDate>+1,<EndDate>)
ELSE -333 END * Datediff(S, Convert(time,<StartTime>),Convert(time,<EndTime>))
这里是功能版本:
CREATE FUNCTION [dbo].[rsf_BusinessTime]
(
@startDateTime Datetime,
@endDateTime Datetime ,
@StartTime VarChar(12),
@EndTime VarChar(12))
RETURNS BIGINT
As
BEGIN
Declare @totalSeconds BigInt,
@SecondsInDay int,
@dayStart Time = Convert(time,@StartTime),
@dayEnd Time =Convert(time,@EndTime),
@SatAfterStart Datetime = dateadd(d,-datepart(dw,@startDateTime),dateadd(ww,1,@startDateTime)),
@Sunbeforend Datetime = DATEADD(wk, DATEDIFF(wk, 6, @endDateTime), 6)
-- This function calculates the seconds between the start and end dates provided for business hours.
-- It only returns the time between the @start and @end time (hour of day) of the work week.
-- Weekend days are removed.
-- Holidays are not considered.
Set @SecondsInDay = Datediff(S, @dayStart,@dayEnd)
Set @totalSeconds =
--first/last/sameday
Case when @startDateTime= @endDateTime then 0
When Convert(date,@startDateTime) = Convert(date,@endDateTime) Then
IIF(DATEPART(Dw,@startDateTime) in(1,7)
or Convert(time,@startDateTime) > @dayEnd
or Convert(time,@endDateTime) < @dayStart,0,
DateDiff(S,IIF(Convert(time,@startDateTime) < @dayStart,@dayStart,Convert(time,@startDateTime))
,IIF(Convert(time,@endDateTime) > @dayEnd, @dayEnd, Convert(time,@endDateTime))))
when Convert(date,@startDateTime) <> Convert(date,@endDateTime) then
IIF(DATEPART(Dw,@startDateTime) in(1,7) or Convert(time,@startDateTime) > @dayEnd,0 ,DateDiff(S,IIF(Convert(time,@startDateTime) < @dayStart,@dayStart,Convert(time,@startDateTime)), @dayEnd))
+ IIF(DATEPART(Dw,@endDateTime) in(1,7) or Convert(time,@endDateTime) < @dayStart,0,DateDiff(S,@dayStart,IIF(Convert(time,@endDateTime) > @dayEnd, @dayEnd, Convert(time,@endDateTime))))
else -333
end --as pday
+IIF(DatePart(wEEk,@startDateTime) = DatePart(wEEk,@endDateTime)
,0, (DateDiff(wk,@SatAfterStart,@Sunbeforend-1) * 5)) * @SecondsInDay --Fullweek_days
+Case When Convert(date,@startDateTime) = Convert(date,@endDateTime) then 0
When DatePart(wEEk,@startDateTime) <> DatePart(wEEk,@endDateTime) then
IIF(datepart(dw,@startDateTime) = 7,0,DateDIFF(DAY,@startDateTime+1,@SatAfterStart)) -- beginFulldays
+IIF(datepart(dw,@endDateTime) = 1,0,DateDIFF(DAY,@Sunbeforend,@endDateTime -1)) --Endfulldays
When DatePart(wEEk,@startDateTime) = DatePart(wEEk,@endDateTime) then
DateDiff(DAY,@startDateTime+1,@endDateTime)
ELSE -333 END * @SecondsInDay
Return @totalSeconds
END
你怎么看待这个解决方案是什么?
不使用循环 “虽然”。
create function dbo.WorkingHoursBetweenDates (@StartDate datetime, @EndDate datetime, @StartTime time, @EndTime time)
returns decimal (10, 2)
as
begin
return
case
when @EndTime < @StartTime or @EndDate < @StartDate then
0
else
round
((dbo.WorkingDaysBetweenDates(@StartDate, @EndDate) -
(dbo.WorkingDaysBetweenDates(@StartDate, @StartDate) *
case
when cast (@StartDate as time) > @EndTime then
1
else
datediff
(mi,
@StartTime
, case
when @StartTime > cast (@StartDate as time) then
@StartTime
else
cast (@StartDate as time)
end
)/
(datediff (mi, @StartTime, @EndTime) + 0.0)
end
) -
(dbo.WorkingDaysBetweenDates(@EndDate, @EndDate) *
case
when cast (@EndDate as time) < @StartTime then
1
else
datediff
(mi,
case
when @EndTime < cast (@EndDate as time) then
@EndTime
else
cast (@EndDate as time)
end,
@EndTime
)/
(datediff (mi, @StartTime, @EndTime) + 0.0)
end
)
) *
(datediff (mi, @StartTime, @EndTime)/60.0), 2
)
end
end
------
create function dbo.WorkingDaysBetweenDates (@StartDate date, @EndDate date)
returns int
as
begin
return
(datediff(dd, @StartDate, @EndDate) + 1) -
(datediff(wk, @StartDate, @EndDate) * 2) -
(case when datename(dw, @StartDate) = 'Sunday' then 1 else 0 end) -
(case when datename(dw, @EndDate) = 'Saturday' then 1 else 0 end) -
(select
count (1)
from
dbo.Tb_Holidays
where
HDate between @StartDate and @EndDate
and datename(dw, HDate) not in ('Sunday', 'Saturday')
)
end
带while循环的2008版本对于我们的工作流程来说太慢了。如果我们在StartDate和FinishDate之间运行几百万次的计算一百万行,上面的循环版本需要10分钟的时间在我们的SQL服务器上。我的版本在1分钟内完成相同的任务。
下面是没有环(2008年测试),供您参考函数:
ALTER FUNCTION dbo.WorkTime (
@EventStartDateTime DATETIME,
@EventEndDateTime DATETIME,
@BusinessStartDateTime DATETIME,
@BusinessEndDateTime DATETIME,
@IncludeWeekend BIT)
RETURNS BIGINT
AS
BEGIN
-- Purpose: Function that calculates seconds of business hours between an event.
-- Allows the calculation to work with (@IncludeWeekend = 1) or without weekends (@IncludeWeekend = 0), that is working days only or seven days a week.
-- Allows the calculation to work for 24x7 business hours
-- Author Mathias Florin
-- Create Date: 2016/11/05
-- Comments: Event StartDateTime (ESDT), Event EndDateTime(EETD)
-- Business StartDateTime (BSDT), Business EndDateTime (BEDT)
-- Event StartHour (ESH), Event EndHour(EEH)
-- Business StartHour (BSH), Business EndHour (BEH)
-- For 24x7 business hours pass these parameters to the function:
-- BusinessStartDateTime (1900-01-01 00:00:00) and BusinessEndDateTime (1900-01-02 00:00:00)
-- Check Statement: SET DATEFIRST 7; SELECT dbo.WorkTime('2016-11-05 08:00:00','2016-11-05 18:00:00','1900-01-01 09:00:00','1900-01-01 17:00:00',1)
-- Different conditions of calculation which are referenced in the SQL code below:
-- Same day incl. weekend - Business not working 24x7:
-- StartDateTime & EndDateTime on same day
-- 1) ESH BSH <-- 0 day --> BEH EEH -> DIFF(BSH,BEH)
-- 2) BSH ESH <-- 0 day --> BEH EEH -> DIFF(ESH,BEH)
-- 3) ESH BSH <-- 0 day --> EEH BEH -> DIFF(BSH,EEH)
-- 4) BSH ESH <-- 0 day --> EEH BEH -> DIFF(ESH,EEH)
-- Different day incl. weekend - Business not working 24x7:
-- ESDT Days in between EETD
-- 5) ESH BSH <-- 1..n day --> BEH EEH -> DIFF(BSH, BEH) (Daydiff(all days) -1) * (BSH,BEH) DIFF(BSH,BEH)
-- 6) BSH ESH <-- 1..n day --> BEH EEH -> DIFF(ESH,BEH) (Daydiff(all days) -1) * (BSH,BEH) DIFF(BSH,BEH)
-- 7) ESH BSH <-- 1..n day --> EEH BEH -> DIFF(BSH, BEH) (Daydiff(all days) -1) * (BSH,BEH) DIFF(BSH,EEH)
-- 8) BSH ESH <-- 1..n day --> EEH BEH -> DIFF(ESH,BEH) (Daydiff(all days) -1) * (BSH,BEH) DIFF(BSH,EEH)
-- Same day excl. weekend - Business not working 24x7:
-- ESDT & EETD on same day
-- 9) ESH BSH <-- 0 day --> BEH EEH -> Workday THEN DIFF(BSH,BEH) ELSE 0 END
-- 10) BSH ESH <-- 0 day --> BEH EEH -> Workday THEN DIFF(ESH,BEH) ELSE 0 END
-- 11) ESH BSH <-- 0 day --> EEH BEH -> Workday THEN DIFF(BSH,EEH) ELSE 0 END
-- 12) BSH ESH <-- 0 day --> EEH BEH -> Workday THEN DIFF(ESH,EEH) ELSE 0 END
--
-- Different day excl. weekend - Business not working 24x7:
-- ESDT | Workdays in between | EETD
-- 13) ESH BSH <-- 1..n day --> BEH EEH -> (Workday THEN DIFF(BSH, BEH) ELSE 0) + ((Daydiff(workdays) -1) * (BSH,BEH)) + (Workday THEN DIFF(BSH,BEH) ELSE 0)
-- 14) BSH ESH <-- 1..n day --> BEH EEH -> (Workday THEN DIFF(ESH,BEH) ELSE 0) + ((Daydiff(workdays) -1) * (BSH,BEH)) + (Workday THEN DIFF(BSH,BEH) ELSE 0)
-- 15) ESH BSH <-- 1..n day --> EEH BEH -> (Workday THEN DIFF(BSH, BEH) ELSE 0) + ((Daydiff(workdays) -1) * (BSH,BEH)) + (Workday THEN DIFF(BSH,EEH) ELSE 0)
-- 16) BSH ESH <-- 1..n day --> EEH BEH -> (Workday THEN DIFF(ESH,BEH) ELSE 0) + ((Daydiff(workdays) -1) * (BSH,BEH)) + (Workday THEN DIFF(BSH,EEH) ELSE 0)
-- Business working 24x7
-- 17) Date difference in seconds between EventStartDateTime and EventEndDateTime
DECLARE @EventStartDateTimeHour TIME; SET @EventStartDateTimeHour = CAST(@EventStartDateTime AS TIME);
DECLARE @EventEndDateTimeHour TIME; SET @EventEndDateTimeHour = CAST(@EventEndDateTime AS TIME);
DECLARE @BusinessStartDateTimeHour TIME; SET @BusinessStartDateTimeHour = CAST(@BusinessStartDateTime AS TIME);
DECLARE @BusinessEndDateTimeHour TIME; SET @BusinessEndDateTimeHour = CAST(@BusinessEndDateTime AS TIME);
DECLARE @EventStartDateTimeIsWorkingDay BIT = CASE
WHEN DATENAME(DW, @EventStartDateTime) IN ('Saturday', 'Sunday') THEN 0
ELSE 1
END
DECLARE @EventEndDateTimeIsWorkingDay BIT = CASE
WHEN DATENAME(DW, @EventEndDateTime) IN ('Saturday', 'Sunday') THEN 0
ELSE 1
END
DECLARE @NextDayEventStartDateTime DATETIME = DATEADD(DAY, 1, @EventStartDateTime);
DECLARE @PreviousDayEventStartDateTime DATETIME = DATEADD(DAY, -1, @EventEndDateTime);
DECLARE @WorkDaysBetweenEventStartDateTimeAndEventEndDateTime INT = (SELECT
(DATEDIFF(dd, @NextDayEventStartDateTime, @PreviousDayEventStartDateTime) + 1)
- ((DATEDIFF(wk, @NextDayEventStartDateTime, @PreviousDayEventStartDateTime)) * 2)
- (CASE
WHEN DATENAME(dw, @NextDayEventStartDateTime) = 'Sunday' THEN 1
ELSE 0
END)
- (CASE
WHEN DATENAME(dw, @PreviousDayEventStartDateTime) = 'Saturday' THEN 1
ELSE 0
END))
DECLARE @Temp BIGINT
SELECT @Temp = MAX(Second)
FROM (SELECT
'Start' AS Grouping
, 0 AS Second
UNION
SELECT
'Condition 1' AS Grouping
, DATEDIFF(SECOND, @BusinessStartDateTimeHour, @BusinessEndDateTimeHour)
WHERE DATEDIFF(DAY, @EventStartDateTime, @EventEndDateTime) = 0
AND @IncludeWeekend = 1
AND DATEDIFF(SECOND, @EventStartDateTimeHour, @BusinessStartDateTimeHour) > 0
AND DATEDIFF(SECOND, @BusinessEndDateTimeHour, @EventEndDateTimeHour) >= 0
AND DATEDIFF(SECOND, @BusinessStartDateTimeHour, @BusinessEndDateTimeHour) > 0
AND DATEDIFF(DAY, @BusinessStartDateTime, @BusinessEndDateTime) = 0
UNION
SELECT
'Condition 2' AS Grouping
, DATEDIFF(SECOND, @EventStartDateTimeHour, @BusinessEndDateTimeHour)
WHERE DATEDIFF(DAY, @EventStartDateTime, @EventEndDateTime) = 0
AND @IncludeWeekend = 1
AND DATEDIFF(SECOND, @EventStartDateTimeHour, @BusinessStartDateTimeHour) <= 0
AND DATEDIFF(SECOND, @BusinessEndDateTimeHour, @EventEndDateTimeHour) >= 0
AND DATEDIFF(SECOND, @BusinessStartDateTimeHour, @BusinessEndDateTimeHour) > 0
AND DATEDIFF(DAY, @BusinessStartDateTime, @BusinessEndDateTime) = 0
UNION
SELECT
'Condition 3' AS Grouping
, DATEDIFF(SECOND, @BusinessStartDateTimeHour, @EventEndDateTimeHour)
WHERE DATEDIFF(DAY, @EventStartDateTime, @EventEndDateTime) = 0
AND @IncludeWeekend = 1
AND DATEDIFF(SECOND, @EventStartDateTimeHour, @BusinessStartDateTimeHour) > 0
AND DATEDIFF(SECOND, @BusinessEndDateTimeHour, @EventEndDateTimeHour) < 0
AND DATEDIFF(SECOND, @BusinessStartDateTimeHour, @BusinessEndDateTimeHour) > 0
AND DATEDIFF(DAY, @BusinessStartDateTime, @BusinessEndDateTime) = 0
UNION
SELECT
'Condition 4' AS Grouping
, DATEDIFF(SECOND, @EventStartDateTimeHour, @EventEndDateTimeHour)
WHERE DATEDIFF(DAY, @EventStartDateTime, @EventEndDateTime) = 0
AND @IncludeWeekend = 1
AND DATEDIFF(SECOND, @EventStartDateTimeHour, @BusinessStartDateTimeHour) <= 0
AND DATEDIFF(SECOND, @BusinessEndDateTimeHour, @EventEndDateTimeHour) < 0
AND DATEDIFF(SECOND, @BusinessStartDateTimeHour, @BusinessEndDateTimeHour) > 0
AND DATEDIFF(DAY, @BusinessStartDateTime, @BusinessEndDateTime) = 0
UNION
SELECT
'Condition 5' AS Grouping
, DATEDIFF(SECOND, @BusinessStartDateTimeHour, @BusinessEndDateTimeHour) + ((DATEDIFF(DAY, @EventStartDateTime, @EventEndDateTime) - 1) * DATEDIFF(SECOND, @BusinessStartDateTimeHour, @BusinessEndDateTimeHour)) + DATEDIFF(SECOND, @BusinessStartDateTimeHour, @BusinessEndDateTimeHour)
WHERE DATEDIFF(DAY, @EventStartDateTime, @EventEndDateTime) > 0
AND @IncludeWeekend = 1
AND DATEDIFF(SECOND, @EventStartDateTimeHour, @BusinessStartDateTimeHour) > 0
AND DATEDIFF(SECOND, @BusinessEndDateTimeHour, @EventEndDateTimeHour) >= 0
AND DATEDIFF(SECOND, @BusinessStartDateTimeHour, @BusinessEndDateTimeHour) > 0
AND DATEDIFF(DAY, @BusinessStartDateTime, @BusinessEndDateTime) = 0
UNION
SELECT
'Condition 6' AS Grouping
, DATEDIFF(SECOND, @EventStartDateTimeHour, @BusinessEndDateTimeHour) + ((DATEDIFF(DAY, @EventStartDateTime, @EventEndDateTime) - 1) * DATEDIFF(SECOND, @BusinessStartDateTimeHour, @BusinessEndDateTimeHour)) + DATEDIFF(SECOND, @BusinessStartDateTimeHour, @BusinessEndDateTimeHour)
WHERE DATEDIFF(DAY, @EventStartDateTime, @EventEndDateTime) > 0
AND @IncludeWeekend = 1
AND DATEDIFF(SECOND, @EventStartDateTimeHour, @BusinessStartDateTimeHour) <= 0
AND DATEDIFF(SECOND, @BusinessEndDateTimeHour, @EventEndDateTimeHour) >= 0
AND DATEDIFF(SECOND, @BusinessStartDateTimeHour, @BusinessEndDateTimeHour) > 0
AND DATEDIFF(DAY, @BusinessStartDateTime, @BusinessEndDateTime) = 0
UNION
SELECT
'Condition 7' AS Grouping
, DATEDIFF(SECOND, @BusinessStartDateTimeHour, @BusinessEndDateTimeHour) + ((DATEDIFF(DAY, @EventStartDateTime, @EventEndDateTime) - 1) * DATEDIFF(SECOND, @BusinessStartDateTimeHour, @BusinessEndDateTimeHour)) + DATEDIFF(SECOND, @BusinessStartDateTimeHour, @EventEndDateTimeHour)
WHERE DATEDIFF(DAY, @EventStartDateTime, @EventEndDateTime) > 0
AND @IncludeWeekend = 1
AND DATEDIFF(SECOND, @EventStartDateTimeHour, @BusinessStartDateTimeHour) > 0
AND DATEDIFF(SECOND, @BusinessEndDateTimeHour, @EventEndDateTimeHour) < 0
AND DATEDIFF(SECOND, @BusinessStartDateTimeHour, @BusinessEndDateTimeHour) > 0
AND DATEDIFF(DAY, @BusinessStartDateTime, @BusinessEndDateTime) = 0
UNION
SELECT
'Condition 8' AS Grouping
, DATEDIFF(SECOND, @EventStartDateTimeHour, @BusinessEndDateTimeHour) + ((DATEDIFF(DAY, @EventStartDateTime, @EventEndDateTime) - 1) * DATEDIFF(SECOND, @BusinessStartDateTimeHour, @BusinessEndDateTimeHour)) + DATEDIFF(SECOND, @BusinessStartDateTimeHour, @EventEndDateTimeHour)
WHERE DATEDIFF(DAY, @EventStartDateTime, @EventEndDateTime) > 0
AND @IncludeWeekend = 1
AND DATEDIFF(SECOND, @EventStartDateTimeHour, @BusinessStartDateTimeHour) <= 0
AND DATEDIFF(SECOND, @BusinessEndDateTimeHour, @EventEndDateTimeHour) < 0
AND DATEDIFF(SECOND, @BusinessStartDateTimeHour, @BusinessEndDateTimeHour) > 0
AND DATEDIFF(DAY, @BusinessStartDateTime, @BusinessEndDateTime) = 0
UNION
SELECT
'Condition 9' AS Grouping
, CASE
WHEN @EventStartDateTimeIsWorkingDay = 1 THEN DATEDIFF(SECOND, @BusinessStartDateTimeHour, @BusinessEndDateTimeHour)
ELSE 0
END
WHERE DATEDIFF(DAY, @EventStartDateTime, @EventEndDateTime) = 0
AND @IncludeWeekend = 0
AND DATEDIFF(SECOND, @EventStartDateTimeHour, @BusinessStartDateTimeHour) > 0
AND DATEDIFF(SECOND, @BusinessEndDateTimeHour, @EventEndDateTimeHour) >= 0
AND DATEDIFF(SECOND, @BusinessStartDateTimeHour, @BusinessEndDateTimeHour) > 0
AND DATEDIFF(DAY, @BusinessStartDateTime, @BusinessEndDateTime) = 0
UNION
SELECT
'Condition 10' AS Grouping
, CASE
WHEN @EventStartDateTimeIsWorkingDay = 1 THEN DATEDIFF(SECOND, @EventStartDateTimeHour, @BusinessEndDateTimeHour)
ELSE 0
END
WHERE DATEDIFF(DAY, @EventStartDateTime, @EventEndDateTime) = 0
AND @IncludeWeekend = 0
AND DATEDIFF(SECOND, @EventStartDateTimeHour, @BusinessStartDateTimeHour) <= 0
AND DATEDIFF(SECOND, @BusinessEndDateTimeHour, @EventEndDateTimeHour) >= 0
AND DATEDIFF(SECOND, @BusinessStartDateTimeHour, @BusinessEndDateTimeHour) > 0
AND DATEDIFF(DAY, @BusinessStartDateTime, @BusinessEndDateTime) = 0
UNION
SELECT
'Condition 11' AS Grouping
, CASE
WHEN @EventStartDateTimeIsWorkingDay = 1 THEN DATEDIFF(SECOND, @BusinessStartDateTimeHour, @EventEndDateTimeHour)
ELSE 0
END
WHERE DATEDIFF(DAY, @EventStartDateTime, @EventEndDateTime) = 0
AND @IncludeWeekend = 0
AND DATEDIFF(SECOND, @EventStartDateTimeHour, @BusinessStartDateTimeHour) > 0
AND DATEDIFF(SECOND, @BusinessEndDateTimeHour, @EventEndDateTimeHour) < 0
AND DATEDIFF(SECOND, @BusinessStartDateTimeHour, @BusinessEndDateTimeHour) > 0
AND DATEDIFF(DAY, @BusinessStartDateTime, @BusinessEndDateTime) = 0
UNION
SELECT
'Condition 12' AS Grouping
, CASE
WHEN @EventStartDateTimeIsWorkingDay = 1 THEN DATEDIFF(SECOND, @EventStartDateTimeHour, @EventEndDateTimeHour)
ELSE 0
END
WHERE DATEDIFF(DAY, @EventStartDateTime, @EventEndDateTime) = 0
AND @IncludeWeekend = 0
AND DATEDIFF(SECOND, @EventStartDateTimeHour, @BusinessStartDateTimeHour) <= 0
AND DATEDIFF(SECOND, @BusinessEndDateTimeHour, @EventEndDateTimeHour) < 0
AND DATEDIFF(SECOND, @BusinessStartDateTimeHour, @BusinessEndDateTimeHour) > 0
AND DATEDIFF(DAY, @BusinessStartDateTime, @BusinessEndDateTime) = 0
UNION
SELECT
'Condition 13' AS Grouping
, CASE
WHEN @EventStartDateTimeIsWorkingDay = 1 THEN DATEDIFF(SECOND, @BusinessStartDateTimeHour, @BusinessEndDateTimeHour)
ELSE 0
END + ((@WorkDaysBetweenEventStartDateTimeAndEventEndDateTime) * DATEDIFF(SECOND, @BusinessStartDateTimeHour, @BusinessEndDateTimeHour)) + CASE
WHEN @EventEndDateTimeIsWorkingDay = 1 THEN DATEDIFF(SECOND, @BusinessStartDateTimeHour, @BusinessEndDateTimeHour)
ELSE 0
END
WHERE DATEDIFF(DAY, @EventStartDateTime, @EventEndDateTime) > 0
AND @IncludeWeekend = 0
AND DATEDIFF(SECOND, @EventStartDateTimeHour, @BusinessStartDateTimeHour) > 0
AND DATEDIFF(SECOND, @BusinessEndDateTimeHour, @EventEndDateTimeHour) >= 0
AND DATEDIFF(SECOND, @BusinessStartDateTimeHour, @BusinessEndDateTimeHour) > 0
AND DATEDIFF(DAY, @BusinessStartDateTime, @BusinessEndDateTime) = 0
UNION
SELECT
'Condition 14' AS Grouping
, CASE
WHEN @EventStartDateTimeIsWorkingDay = 1 THEN DATEDIFF(SECOND, @EventStartDateTimeHour, @BusinessEndDateTimeHour)
ELSE 0
END + ((@WorkDaysBetweenEventStartDateTimeAndEventEndDateTime) * DATEDIFF(SECOND, @BusinessStartDateTimeHour, @BusinessEndDateTimeHour)) + CASE
WHEN @EventEndDateTimeIsWorkingDay = 1 THEN DATEDIFF(SECOND, @BusinessStartDateTimeHour, @BusinessEndDateTimeHour)
ELSE 0
END
WHERE DATEDIFF(DAY, @EventStartDateTime, @EventEndDateTime) > 0
AND @IncludeWeekend = 0
AND DATEDIFF(SECOND, @EventStartDateTimeHour, @BusinessStartDateTimeHour) <= 0
AND DATEDIFF(SECOND, @BusinessEndDateTimeHour, @EventEndDateTimeHour) >= 0
AND DATEDIFF(SECOND, @BusinessStartDateTimeHour, @BusinessEndDateTimeHour) > 0
AND DATEDIFF(DAY, @BusinessStartDateTime, @BusinessEndDateTime) = 0
UNION
SELECT
'Condition 15' AS Grouping
, CASE
WHEN @EventStartDateTimeIsWorkingDay = 1 THEN DATEDIFF(SECOND, @BusinessStartDateTimeHour, @BusinessEndDateTimeHour)
ELSE 0
END + ((@WorkDaysBetweenEventStartDateTimeAndEventEndDateTime) * DATEDIFF(SECOND, @BusinessStartDateTimeHour, @BusinessEndDateTimeHour)) + CASE
WHEN @EventEndDateTimeIsWorkingDay = 1 THEN DATEDIFF(SECOND, @BusinessStartDateTimeHour, @EventEndDateTimeHour)
ELSE 0
END
WHERE DATEDIFF(DAY, @EventStartDateTime, @EventEndDateTime) > 0
AND @IncludeWeekend = 0
AND DATEDIFF(SECOND, @EventStartDateTimeHour, @BusinessStartDateTimeHour) > 0
AND DATEDIFF(SECOND, @BusinessEndDateTimeHour, @EventEndDateTimeHour) < 0
AND DATEDIFF(SECOND, @BusinessStartDateTimeHour, @BusinessEndDateTimeHour) > 0
AND DATEDIFF(DAY, @BusinessStartDateTime, @BusinessEndDateTime) = 0
UNION
SELECT
'Condition 16' AS Grouping
, CASE
WHEN @EventStartDateTimeIsWorkingDay = 1 THEN DATEDIFF(SECOND, @EventStartDateTimeHour, @BusinessEndDateTimeHour)
ELSE 0
END + ((@WorkDaysBetweenEventStartDateTimeAndEventEndDateTime) * DATEDIFF(SECOND, @BusinessStartDateTimeHour, @BusinessEndDateTimeHour)) + CASE
WHEN @EventEndDateTimeIsWorkingDay = 1 THEN DATEDIFF(SECOND, @BusinessStartDateTimeHour, @EventEndDateTimeHour)
ELSE 0
END
WHERE DATEDIFF(DAY, @EventStartDateTime, @EventEndDateTime) > 0
AND @IncludeWeekend = 0
AND DATEDIFF(SECOND, @EventStartDateTimeHour, @BusinessStartDateTimeHour) <= 0
AND DATEDIFF(SECOND, @BusinessEndDateTimeHour, @EventEndDateTimeHour) < 0
AND DATEDIFF(SECOND, @BusinessStartDateTimeHour, @BusinessEndDateTimeHour) > 0
AND DATEDIFF(DAY, @BusinessStartDateTime, @BusinessEndDateTime) = 0
UNION
SELECT
'Condition 17' AS Grouping
, DATEDIFF(SECOND, @EventStartDateTime, @EventEndDateTime)
WHERE DATEDIFF(DAY, @BusinessStartDateTime, @BusinessEndDateTime) > 0) AS SecondsPassed
RETURN @Temp
END
mathias florin的解决方案干净简单,但存在条件16的错误。如果使用营业时间8 5,活动开始时间是下午5点以后,第二天上午9点结束。条件16的另一个失败是如果在下午5点后开始并在第二天上午8点之前完成。 – Joel 2016-11-11 15:37:39
@Joel:关于第一次失败:如果事件在17:15:00开始,第二天在09:00:00结束,营业时间为08:00:00至17:00:00,则函数返回3600秒这是一个小时。这是正确的,因为企业有机会在早上(08:00-09:00)在活动中工作一小时(3600秒)。 关于第二次失败,如果事件在营业时间之后开始并在早上营业时间之前结束,则条件16返回-3600。结果与0(cond。starting)结合,返回最大值。 MAX(-3600,0)返回0,这是正确的 - 没有时间花在事件上。 – 2016-11-14 14:22:38
的问题说,公众假期不应该被考虑,所以这个回答做到了这一点 - 计算工作时间服用周末但忽略可能的公众假期。
它还假设给定的开始和结束日期/时间是在营业时间。
有了这个假设,代码并不关心营业日开始或结束的时间,它只关心每天的营业时间总数。在你的例子中,从09:00到17:00有8个营业时间。它不一定是一个整数。下面的公式用一分钟的精度来计算它,但是使它达到一秒或者其他任何精度都是微不足道的。
如果您需要乘坐公共假期到你需要有一个单独的表,该表将列出日期为公众假期,这可能会有所不同每年从州或国家的。主要公式可能保持不变,但您需要在规定日期范围内的公共假期中扣除结果小时。
公式
SELECT
DATEDIFF(minute, StartDT, EndDT)/60.0
- DATEDIFF(day, StartDT, EndDT) * 16
- DATEDIFF(week, StartDT, EndDT) * 16 AS BusinessHours
FROM T
要了解它的工作原理是如何让我们创建一个表,涵盖各种情况下,一些样本数据:
DECLARE @T TABLE (StartDT datetime2(0), EndDT datetime2(0));
INSERT INTO @T VALUES
('2012-03-05 09:00:00', '2012-03-05 15:00:00'), -- simple part of the same day
('2012-03-05 10:00:00', '2012-03-06 10:00:00'), -- full day across the midnight
('2012-03-05 11:00:00', '2012-03-06 10:00:00'), -- less than a day across the midnight
('2012-03-05 10:00:00', '2012-03-06 15:00:00'), -- more than a day across the midnight
('2012-03-09 16:00:00', '2012-03-12 10:00:00'), -- over the weekend, less than 7 days
('2012-03-06 16:00:00', '2012-03-15 10:00:00'), -- over the weekend, more than 7 days
('2012-03-09 16:00:00', '2012-03-19 10:00:00'); -- over two weekends
查询
SELECT
StartDT,
EndDT,
DATEDIFF(minute, StartDT, EndDT)/60.0
- DATEDIFF(day, StartDT, EndDT) * 16
- DATEDIFF(week, StartDT, EndDT) * 16 AS BusinessHours
FROM @T;
产生以下结果:
+---------------------+---------------------+---------------+
| StartDT | EndDT | BusinessHours |
+---------------------+---------------------+---------------+
| 2012-03-05 09:00:00 | 2012-03-05 15:00:00 | 6.000000 |
| 2012-03-05 10:00:00 | 2012-03-06 10:00:00 | 8.000000 |
| 2012-03-05 11:00:00 | 2012-03-06 10:00:00 | 7.000000 |
| 2012-03-05 10:00:00 | 2012-03-06 15:00:00 | 13.000000 |
| 2012-03-09 16:00:00 | 2012-03-12 10:00:00 | 2.000000 |
| 2012-03-06 16:00:00 | 2012-03-15 10:00:00 | 50.000000 |
| 2012-03-09 16:00:00 | 2012-03-19 10:00:00 | 42.000000 |
+---------------------+---------------------+---------------+
它的工作原理,因为在SQL Server DATEDIFF
返回指定开始日期和结束日期之间交叉指定的日期部分边界的计数。
每天有8个营业时间。我计算两个日期之间的总小时数,然后减去每天中午乘以16个非营业时间的数量,然后减去周末数乘以16(周六+周日为8 + 8个营业时间)。
这是另一种解决方案,不使用函数。请注意,这依赖于numbers table的存在,并且至少填充您正在跟踪的任务可能需要的最长天数。
这不考虑公众假期。如果你周末不工作,在@OpeningHours表变量中设置开始和结束时间至午夜,应该完成这项工作。
我已经测试了8500行“真实世界”数据,并发现它是高性能的。
DECLARE @OpeningHours TABLE ([DayOfWeek] INTEGER, OpeningTime TIME(0), ClosingTime TIME(0));
INSERT
@OpeningHours ([DayOfWeek], OpeningTime, ClosingTime)
VALUES
(1, '10:00', '16:00') -- Sun
, (2, '06:30', '23:00') -- Mon
, (3, '06:30', '23:00') -- Tue
, (4, '06:30', '23:00') -- Wed
, (5, '06:30', '23:00') -- Thu
, (6, '06:30', '23:00') -- Fri
, (7, '08:00', '20:00'); -- Sat
DECLARE @Tasks TABLE ([Description] VARCHAR(50), CreatedDateTime DATETIME, CompletedDateTime DATETIME);
INSERT
@Tasks ([Description], CreatedDateTime, CompletedDateTime)
VALUES
('Make tea', '20170404 10:00', '20170404 10:12')
, ('Make coffee', '20170404 23:35', '20170405 06:32')
, ('Write complex SQL query', '20170406 00:00', '20170406 23:32')
, ('Rewrite complex SQL query', '20170406 23:50', '20170410 10:50');
SELECT
WorkingMinutesToRespond =
SUM(CASE WHEN CAST(Tasks.CreatedDateTime AS DATE) = CAST(Tasks.CompletedDateTime AS DATE) THEN
CASE WHEN CAST(Tasks.CreatedDateTime AS TIME) < OpeningHours.OpeningTime THEN
-- Task created before opening time
DATEDIFF(MINUTE, OpeningHours.OpeningTime, CAST(Tasks.CompletedDateTime AS TIME))
ELSE
DATEDIFF(MINUTE, Tasks.CreatedDateTime, Tasks.CompletedDateTime)
END
ELSE
CASE WHEN Tasks.CoveredDate = CAST(Tasks.CreatedDateTime AS DATE) THEN
-- This is the day the task was created
CASE WHEN CAST(Tasks.CreatedDateTime AS TIME(0)) > OpeningHours.ClosingTime THEN
0 -- after working hours
ELSE
-- during or before working hours
CASE WHEN CAST(Tasks.CreatedDateTime AS TIME(0)) < OpeningHours.OpeningTime THEN
-- before opening time; take the whole day into account
DATEDIFF(MINUTE, OpeningHours.OpeningTime, OpeningHours.ClosingTime)
ELSE
-- during opening hours; take part of the day into account
DATEDIFF(MINUTE, CAST(Tasks.CreatedDateTime AS TIME), OpeningHours.ClosingTime)
END
END
ELSE
-- This is the day the task was completed
CASE WHEN Tasks.CoveredDate = CAST(Tasks.CompletedDateTime AS DATE) THEN
CASE WHEN CAST(Tasks.CompletedDateTime AS TIME(0)) < OpeningHours.OpeningTime THEN
0 -- before working hours (unlikely to occur)
ELSE
-- during or after working hours
CASE WHEN CAST(Tasks.CompletedDateTime AS TIME(0)) > OpeningHours.ClosingTime THEN
-- after closing time (also unlikely); take the whole day into account
DATEDIFF(MINUTE, OpeningHours.OpeningTime, OpeningHours.ClosingTime)
ELSE
-- during opening hours; take part of the day into account
DATEDIFF(MINUTE, OpeningHours.OpeningTime, CAST(Tasks.CompletedDateTime AS TIME(0)))
END
END
ELSE
DATEDIFF(MINUTE, OpeningHours.OpeningTime, OpeningHours.ClosingTime)
END
END
END)
, Tasks.Description
, Tasks.CreatedDateTime
, Tasks.CompletedDateTime
FROM
(
SELECT
Tasks.Description
, Tasks.CreatedDateTime
, Tasks.CompletedDateTime
, CoveredDate = CAST(DATEADD(DAY, Numbers.Number, Tasks.CreatedDateTime) AS DATE)
FROM
@Tasks Tasks
INNER JOIN (SELECT * FROM Numbers WHERE Number >= 0) Numbers ON DATEDIFF(DAY, Tasks.CreatedDateTime, Tasks.CompletedDateTime) >= Numbers.Number
) Tasks
INNER JOIN @OpeningHours OpeningHours ON DATEPART(WEEKDAY, Tasks.CoveredDate) = OpeningHours.[DayOfWeek]
GROUP BY
Tasks.Description
, Tasks.CreatedDateTime
, Tasks.CompletedDateTime
ORDER BY
Tasks.CompletedDateTime;
第一步是计算工作日内,如示于下面的脚本:
DECLARE @TotalWorkDays INT, @TotalTimeDiff DECIMAL(18, 2), @DateFrom DATETIME, @DateTo DATETIME;
SET @DateFrom = '2017-06-05 11:19:11.287';
SET @DateTo = '2017-06-07 09:53:14.750';
SET @TotalWorkDays = DATEDIFF(DAY, @DateFrom, @DateTo)
-(DATEDIFF(WEEK, @DateFrom, @DateTo) * 2)
-CASE
WHEN DATENAME(WEEKDAY, @DateFrom) = 'Sunday'
THEN 1
ELSE 0
END+CASE
WHEN DATENAME(WEEKDAY, @DateTo) = 'Saturday'
THEN 1
ELSE 0
END;
第二步骤包括得到一个差,以秒为两个日期之间和差转换成小时通过如本下列脚本由3600.0分割:
SET @TotalTimeDiff =
(
SELECT DATEDIFF(SECOND,
(
SELECT CONVERT(TIME, @DateFrom)
),
(
SELECT CONVERT(TIME, @DateTo)
))/3600.0
);
最后部分包括通过24(的总小时数的输出乘以上述第一步骤i呐天),然后后来并称到第二步骤的输出:
SELECT(@TotalWorkDays * 24.00) + @TotalTimeDiff;
最后,可用于创建用于计算的工作时间在用户定义函数的完整脚本如下所示:
CREATE FUNCTION [dbo].[fn_GetTotalWorkingHours]
(
@DateFrom Datetime,
@DateTo Datetime
)
RETURNS DECIMAL(18,2)
AS
BEGIN
DECLARE @TotalWorkDays INT, @TotalTimeDiff DECIMAL(18, 2)
SET @TotalWorkDays = DATEDIFF(DAY, @DateFrom, @DateTo)
-(DATEDIFF(WEEK, @DateFrom, @DateTo) * 2)
-CASE
WHEN DATENAME(WEEKDAY, @DateFrom) = 'Sunday'
THEN 1
ELSE 0
END+CASE
WHEN DATENAME(WEEKDAY, @DateTo) = 'Saturday'
THEN 1
ELSE 0
END;
SET @TotalTimeDiff =
(
SELECT DATEDIFF(SECOND,
(
SELECT CONVERT(TIME, @DateFrom)
),
(
SELECT CONVERT(TIME, @DateTo)
))/3600.0
);
RETURN (SELECT(@TotalWorkDays * 24.00) + @TotalTimeDiff)
END
GO
完整的方法,在这篇文章中descibed: https://www.sqlshack.com/how-to-calculate-work-days-and-hours-in-sql-server/
- 1. 如何计算两个日期之间的营业日数?
- 2. 计算两个日期之间的营业日数?
- 3. 最快的算法来计算两个日期之间的营业日数?
- 4. 计算两天之间的营业日应排除假期
- 5. 计算两个日期之间的日间时间和夜间时间
- 6. 计算不同行两个日期之间的时间间隔
- 7. 日期间的计算在两个日期之间
- 8. 计算在pyspark的两个日期之间的时间
- 9. 计算sql中两个日期之间的持续时间
- 10. 计算两个日期之间的工作时间
- 11. 如何计算两个日期之间的时间差? - MySql
- 12. 计算MVC中两个日期时间之间的差异5
- 13. 计算两个日期之间的时间?
- 14. JScript:如何计算两个日期时间之间的差异?
- 15. PHP计算两个日期之间的办公时间数
- 16. 如何计算两个日期之间的时间差 - MySql
- 17. 计算两个日期之间的时间
- 18. 计算两个日期时间之间的角度
- 19. 计算每天两个日期之间的时间
- 20. ActionScript 3.0 +计算两个日期之间的时间跨度?
- 21. 如何计算两个日期之间的时间段 - angularjs
- 22. 计算日期之间的时间差
- 23. 计算两天之间的日期 - J2ME
- 24. 计算两个日期时间
- 25. VB.NET - 计算排除日期之间的两个日期之间的天数
- 26. 计算所有记录中两个日期时间之间的平均时间?
- 27. 如何计算和计算两个日期之间的字段?
- 28. 我如何计算两个日期之间的日期
- 29. 如何使用PHP计算两个日期之间的日期?
- 30. 我需要计算一个日期时间列之间的日期/时间差
我假设你并不需要采取节假日等考虑在内? – Blorgbeard 2011-03-11 14:37:06
...什么午餐休息时间,茶歇等? – 2011-03-11 14:48:06
是的,我不采取国家假期和休息。 – Baran 2011-03-11 18:46:54