tiempo - sql server diferencia entre 2 fechas
Calcule el horario comercial entre dos fechas (14)
¿Qué piensas de esta solución?
Sin usar el bucle "While".
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
¿Cómo puedo calcular el horario comercial entre dos fechas? Por ejemplo, tenemos dos fechas; 01/01/2010 15:00 y 01/04/2010 12:00 Y tenemos horario de trabajo de 09:00 a 17:00 de lunes a viernes. ¿Cómo puedo calcular las horas de trabajo con sql?
Aquí hay una solución alternativa, sin el uso de una función. Tenga en cuenta que esto depende de la existencia de una tabla de números , poblada con al menos el número máximo de días que pueden llevar las tareas que realiza el seguimiento.
Esto no toma en cuenta los días festivos. Si no trabaja los fines de semana, establecer las horas de apertura y cierre a medianoche en la variable de tabla @OpeningHours debería hacer el trabajo.
Probé esto con 8500 filas de datos del "mundo real" y descubrí que funcionaba.
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;
Aquí hay una versión en línea Start / EndDateTime como 2015-03-16 09: 52: 24.000 Start / EndTime (businesshours) como 07:00:00 Es abultada pero funciona en su declaración seleccionada
Lo publicaré en la versión de Function también.
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>))
Aquí está la versión de la función:
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
De hecho, he hecho esto antes, teniendo en cuenta todas las variables (fines de semana, vacaciones, etc.) para las horas de oficina es muy difícil, creo que esta tarea se realiza mejor fuera de SQL
El primer paso sería calcular los días hábiles, como se muestra en el siguiente script:
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;
El segundo paso implica obtener una diferencia en segundos entre las dos fechas y convertir esa diferencia en horas dividiendo por 3600.0 como se muestra en este siguiente script:
SET @TotalTimeDiff =
(
SELECT DATEDIFF(SECOND,
(
SELECT CONVERT(TIME, @DateFrom)
),
(
SELECT CONVERT(TIME, @DateTo)
)) / 3600.0
);
La última parte implica multiplicar el resultado del primer paso anterior por 24 (número total de horas en un día) y luego agregarlo al resultado del segundo paso:
SELECT(@TotalWorkDays * 24.00) + @TotalTimeDiff;
Finalmente, la secuencia de comandos completa que se puede utilizar para crear una función definida por el usuario para calcular las horas de trabajo se muestra a continuación:
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
El método completo se describe en este artículo: https://www.sqlshack.com/how-to-calculate-work-days-and-hours-in-sql-server/
La pregunta dice que los días festivos no deben ser considerados, por lo que esta respuesta hace precisamente eso: calcula las horas de trabajo teniendo en cuenta los fines de semana, pero ignora los posibles días festivos.
También asume que las fechas / horas de inicio y finalización son durante el horario comercial.
Con esta suposición, al código no le importa cuándo comienza o termina el día hábil, solo le importa el número total de horas de trabajo por día. En su ejemplo, hay 8 horas hábiles entre las 09:00 y las 17:00. No tiene que ser un número entero. La siguiente fórmula lo calcula con precisión de un minuto, pero es trivial hacerlo de un segundo o de cualquier otra precisión.
Si necesita tomar en cuenta los días festivos, necesitará tener una tabla separada que enumere las fechas de los días festivos, que pueden diferir de un año a otro y de un estado a otro o de un país a otro. La fórmula principal puede seguir siendo la misma, pero deberá restar de sus horas de resultados los días festivos que caen dentro del rango de fechas especificado.
La formula
SELECT
DATEDIFF(minute, StartDT, EndDT) / 60.0
- DATEDIFF(day, StartDT, EndDT) * 16
- DATEDIFF(week, StartDT, EndDT) * 16 AS BusinessHours
FROM T
Para comprender cómo funciona, creamos una tabla con algunos datos de muestra que cubren varios casos:
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
La consulta
SELECT
StartDT,
EndDT,
DATEDIFF(minute, StartDT, EndDT) / 60.0
- DATEDIFF(day, StartDT, EndDT) * 16
- DATEDIFF(week, StartDT, EndDT) * 16 AS BusinessHours
FROM @T;
produce el siguiente resultado:
+---------------------+---------------------+---------------+
| 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 |
+---------------------+---------------------+---------------+
Funciona, porque en SQL Server DATEDIFF
devuelve el recuento de los límites de fecha y hora especificados cruzados entre la fecha de inicio y la fecha de finalización especificadas.
Cada día tiene 8 horas hábiles. Calculo el número total de horas entre dos fechas, luego restamos el número de medias noches multiplicado por 16 horas no comerciales por día, luego restamos el número de fines de semana multiplicado por 16 (8 + 8 horas hábiles para Sat + Sun).
La respuesta de Baran fue arreglada y modificada para SQL 2005
SQL 2008 y superior:
-- =============================================
-- 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 @FinishTime=@WorkFinish
END
IF (@FinishTime<@WorkStart)
BEGIN
SET @FinishTime=@WorkStart
END
IF (@StartTime>@WorkFinish)
BEGIN
SET @StartTime = @WorkFinish
END
DECLARE @CurrentDate DATE
SET @CurrentDate = @FirstDay
DECLARE @LastDate DATE
SET @LastDate = @LastDay
WHILE(@CurrentDate<=@LastDate)
BEGIN
IF (DATEPART(dw, @CurrentDate)!=1 AND DATEPART(dw, @CurrentDate)!=7)
BEGIN
IF (@CurrentDate!=@FirstDay) AND (@CurrentDate!=@LastDay)
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 (@CurrentDate=@FirstDay) AND (@CurrentDate!=@LastDay)
BEGIN
SET @Temp = @Temp + DATEDIFF(MINUTE, @StartTime, @WorkFinish)
END
ELSE IF (@CurrentDate!=@FirstDay) AND (@CurrentDate=@LastDay)
BEGIN
SET @Temp = @Temp + DATEDIFF(MINUTE, @WorkStart, @FinishTime)
END
--IF it starts and finishes in the same date
ELSE IF (@CurrentDate=@FirstDay) AND (@CurrentDate=@LastDay)
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 y abajo:
-- =============================================
-- 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 @FinishTime=@WorkFinish
END
IF (@FinishTime<@WorkStart)
BEGIN
SET @FinishTime=@WorkStart
END
IF (@StartTime>@WorkFinish)
BEGIN
SET @StartTime = @WorkFinish
END
DECLARE @CurrentDate DATETIME
SET @CurrentDate = @FirstDay
DECLARE @LastDate DATETIME
SET @LastDate = @LastDay
WHILE(@CurrentDate<=@LastDate)
BEGIN
IF (DATEPART(dw, @CurrentDate)!=1 AND DATEPART(dw, @CurrentDate)!=7)
BEGIN
IF (@CurrentDate!=@FirstDay) AND (@CurrentDate!=@LastDay)
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 (@CurrentDate=@FirstDay) AND (@CurrentDate!=@LastDay)
BEGIN
SET @Temp = @Temp + DATEDIFF(MINUTE, @StartTime, @WorkFinish)
END
ELSE IF (@CurrentDate!=@FirstDay) AND (@CurrentDate=@LastDay)
BEGIN
SET @Temp = @Temp + DATEDIFF(MINUTE, @WorkStart, @FinishTime)
END
--IF it starts and finishes in the same date
ELSE IF (@CurrentDate=@FirstDay) AND (@CurrentDate=@LastDay)
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
La versión 2008 con el ciclo while es demasiado lenta para nuestro flujo de trabajo. La versión de ciclo anterior toma diez minutos en nuestro servidor SQL si ejecutamos el cálculo con varios años entre StartDate y FinishDate para un millón de filas. Mi versión completa la misma tarea en 1 minuto.
A continuación se muestra la función sin bucle (probado en 2008) para su referencia:
CREATE 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
IF DATEDIFF(SECOND, @BusinessEndDateTimeHour, @EventStartDateTimeHour) > 0
BEGIN
SET @TEMP = @TEMP + DATEDIFF(SECOND, @BusinessEndDateTimeHour, @EventStartDateTimeHour)
END
IF DATEDIFF(SECOND, @EventEndDateTimeHour, @BusinessStartDateTimeHour) > 0
BEGIN
SET @TEMP = @TEMP + DATEDIFF(SECOND, @EventEndDateTimeHour, @BusinessStartDateTimeHour)
END
RETURN @Temp
END
GO
Sé que esta publicación es muy antigua, pero he aquí una función que escribí recientemente para calcular los horarios comerciales / minutos entre dos eventos. También tiene en cuenta las vacaciones que deben definirse en una tabla.
La función devuelve el intervalo en minutos; puede dividir por 60 para obtener horas según sea necesario.
Esto ha sido probado en SQL Server 2008. Espero que ayude a alguien.
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 DateTime=@StartDate
Set @StartDate=@EndDate
Set @EndDate=@TempDate
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 COUNTRYCODE=@Country 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)>=@EndHour+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)>=@EndHour+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 @WorkMin=@WorkMin + 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 @WorkMin=@WorkMin + (@EndHour-@StartHour)*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 @WorkMin=@WorkMin + DATEDIFF(MI, @StartDate, @EndDate) Else Set @WorkMin=@WorkMin + DATEDIFF(MI, DateAdd(hour, @StartHour, DateDiff(DAY, 0, @StartDate)), @EndDate)
End
If @Reverse=1 Set @WorkMin=-@WorkMin
Return @WorkMin
End
Una solución alternativa de @ Pavanred, viniendo desde un ángulo más basado en datos:
Crea una tabla con todas las fechas que quieras considerar en ella. Para cada día, establezca un número de horas de trabajo, de esta manera:
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.
Esto requerirá una pequeña cantidad de configuración: puede rellenarlo previamente durante semanas versus fines de semana de forma automática, luego ajustarlo para días festivos, etc., según sea necesario.
Pero, lo que pierdes en la configuración, obtienes facilidad de consulta:
SELECT
SUM(Hours)
FROM
working_days
WHERE
WorkingDate BETWEEN @StartDate AND @EndDate
... y esto puede funcionar como un enfoque más fácil si necesita comenzar a agregar reglas más complicadas para lo que define un día laboral, o si sus horas de trabajo varían según el día, etc.
También hace que las reglas sean más fáciles de "editar", ya que no necesita cambiar ningún código real para cambiar las definiciones de un día de trabajo, agregar días festivos, etc.
Otra forma de pensar , la siguiente función funciona correctamente si su primer día de la semana es el lunes; de lo contrario, debería cambiar las líneas relacionadas, incluyendo (6,7) los días de su fin de semana local.
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 =@FinishTime - @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 @minutes=@minutes+@WorkDayLong
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 @lastDayMinutes=@WorkDayLong
if @Date1<>@Date2
set @minutes=@minutes+@lastDayMinutes
Else
Set @minutes=@minutes+@lastDayMinutes-@WorkDayLong
End
return @minutes
End
-- =============================================
-- 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 @FinishTime=@WorkFinish
END
DECLARE @CurrentDate DATE
SET @CurrentDate = CONVERT(DATE, @StartDate, 112)
DECLARE @LastDate DATE
SET @LastDate = CONVERT(DATE, @FinishDate, 112)
WHILE(@CurrentDate<=@LastDate)
BEGIN
IF (DATEPART(dw, @CurrentDate)!=1 AND DATEPART(dw, @CurrentDate)!=7)
BEGIN
IF (@CurrentDate!=@FirstDay) AND (@CurrentDate!=@LastDay)
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 (@CurrentDate=@FirstDay) AND (@CurrentDate!=@LastDay)
BEGIN
SET @Temp = @Temp + DATEDIFF(MINUTE, @StartTime, @WorkFinish)
END
ELSE IF (@CurrentDate!=@FirstDay) AND (@CurrentDate=@LastDay)
BEGIN
SET @Temp = @Temp + DATEDIFF(MINUTE, @WorkStart, @FinishTime)
END
--IF it starts and finishes in the same date
ELSE IF (@CurrentDate=@FirstDay) AND (@CurrentDate=@LastDay)
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 @FinishTime=@WorkFinish
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<=@LastDate)
BEGIN
IF (DATEPART(dw, @CurrentDate)!=1 AND DATEPART(dw, @CurrentDate)!=7)
BEGIN
IF (@CurrentDate!=@FirstDay) AND (@CurrentDate!=@LastDay)
BEGIN
SET @Temp = (@Temp + (8*60))
END
ELSE IF (@CurrentDate=@FirstDay) AND (@CurrentDate!=@LastDay)
BEGIN
SET @Temp = @Temp + DATEDIFF(MINUTE, @StartTime, @WorkFinish)
END
ELSE IF (@CurrentDate!=@FirstDay) AND (@CurrentDate=@LastDay)
BEGIN
SET @Temp = @Temp + DATEDIFF(MINUTE, @WorkStart, @FinishTime)
END
ELSE IF (@CurrentDate=@FirstDay) AND (@CurrentDate=@LastDay)
BEGIN
SET @Temp = DATEDIFF(MINUTE, @StartTime, @FinishTime)
END
END
SET @CurrentDate = CONVERT(VARCHAR(9),DATEADD(day, 1, @CurrentDate),112)
END
Return @TEMP
END
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