¿Cómo puedo calcular el número de días de trabajo entre dos fechas en SQL Server?Contabilizar días de trabajo entre dos fechas
de lunes a viernes y debe ser T-SQL.
¿Cómo puedo calcular el número de días de trabajo entre dos fechas en SQL Server?Contabilizar días de trabajo entre dos fechas
de lunes a viernes y debe ser T-SQL.
Para los días laborables, de lunes a viernes, puede hacerlo con un solo SELECT, así:
DECLARE @StartDate DATETIME
DECLARE @EndDate DATETIME
SET @StartDate = '2008/10/01'
SET @EndDate = '2008/10/31'
SELECT
(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)
Si desea incluir las vacaciones, usted tiene que trabajar hacia fuera un poco ...
nice answer +1 :) –
¡Me acabo de dar cuenta de que este código no funciona siempre! Intenté esto: SET @StartDate = '28 -mar-2011 ' SET @EndDate = '29 -mar-2011' la respuesta lo contó como 2 días – greektreat
@greektreat Funciona bien. Es solo que tanto @StartDate como @EndDate están incluidos en el recuento. Si desea contar de lunes a martes como 1 día, simplemente elimine el "+1" después del primer DATEDIFF. Entonces también obtendrás Fri-> Sat = 0, Fri-> Sol = 0, Fri-> Mon = 1. –
En Calculating Work Days puede encontrar un buen artículo sobre este tema, pero como puede ver no está tan avanzado.
--Changing current database to the Master database allows function to be shared by everyone.
USE MASTER
GO
--If the function already exists, drop it.
IF EXISTS
(
SELECT *
FROM dbo.SYSOBJECTS
WHERE ID = OBJECT_ID(N'[dbo].[fn_WorkDays]')
AND XType IN (N'FN', N'IF', N'TF')
)
DROP FUNCTION [dbo].[fn_WorkDays]
GO
CREATE FUNCTION dbo.fn_WorkDays
--Presets
--Define the input parameters (OK if reversed by mistake).
(
@StartDate DATETIME,
@EndDate DATETIME = NULL [email protected] replaced by @StartDate when DEFAULTed
)
--Define the output data type.
RETURNS INT
AS
--Calculate the RETURN of the function.
BEGIN
--Declare local variables
--Temporarily holds @EndDate during date reversal.
DECLARE @Swap DATETIME
--If the Start Date is null, return a NULL and exit.
IF @StartDate IS NULL
RETURN NULL
--If the End Date is null, populate with Start Date value so will have two dates (required by DATEDIFF below).
IF @EndDate IS NULL
SELECT @EndDate = @StartDate
--Strip the time element from both dates (just to be safe) by converting to whole days and back to a date.
--Usually faster than CONVERT.
--0 is a date (01/01/1900 00:00:00.000)
SELECT @StartDate = DATEADD(dd,DATEDIFF(dd,0,@StartDate), 0),
@EndDate = DATEADD(dd,DATEDIFF(dd,0,@EndDate) , 0)
--If the inputs are in the wrong order, reverse them.
IF @StartDate > @EndDate
SELECT @Swap = @EndDate,
@EndDate = @StartDate,
@StartDate = @Swap
--Calculate and return the number of workdays using the input parameters.
--This is the meat of the function.
--This is really just one formula with a couple of parts that are listed on separate lines for documentation purposes.
RETURN (
SELECT
--Start with total number of days including weekends
(DATEDIFF(dd,@StartDate, @EndDate)+1)
--Subtact 2 days for each full weekend
-(DATEDIFF(wk,@StartDate, @EndDate)*2)
--If StartDate is a Sunday, Subtract 1
-(CASE WHEN DATENAME(dw, @StartDate) = 'Sunday'
THEN 1
ELSE 0
END)
--If EndDate is a Saturday, Subtract 1
-(CASE WHEN DATENAME(dw, @EndDate) = 'Saturday'
THEN 1
ELSE 0
END)
)
END
GO
Si necesita utilizar un calendario personalizado, es posible que necesite agregar algunos controles y algunos parámetros. Con suerte, proporcionará un buen punto de partida.
Gracias por incluir el enlace para entender cómo funciona esto. ¡La escritura en sqlservercentral fue genial! –
Gracias por los comentarios en línea. Me ayudaron a ver rápidamente cómo funciona esto. – unnknown
DECLARE @TotalDays INT,@WorkDays INT
DECLARE @ReducedDayswithEndDate INT
DECLARE @WeekPart INT
DECLARE @DatePart INT
SET @TotalDays= DATEDIFF(day, @StartDate, @EndDate) +1
SELECT @ReducedDayswithEndDate = CASE DATENAME(weekday, @EndDate)
WHEN 'Saturday' THEN 1
WHEN 'Sunday' THEN 2
ELSE 0 END
SET @[email protected]@ReducedDayswithEndDate
SET @[email protected]/7;
SET @[email protected]%7;
SET @WorkDays=(@WeekPart*5)[email protected]
RETURN @WorkDays
Si publica código, XML o muestras de datos, ** por favor ** resalte esas líneas en el editor de texto y haga clic en el botón "muestras de código" ({}) en la barra de herramientas del editor para formatear y sintaxis ¡destaquelo! –
Este es realmente muy bueno --- ¡gracias! – Dylon
Excelente, sin necesidad de funciones de periferia o actualizaciones de la base de datos usando esto. Gracias. Love the saltire por cierto :-) –
DECLARE @StartDate datetime,@EndDate datetime
select @StartDate='3/2/2010', @EndDate='3/7/2010'
DECLARE @TotalDays INT,@WorkDays INT
DECLARE @ReducedDayswithEndDate INT
DECLARE @WeekPart INT
DECLARE @DatePart INT
SET @TotalDays= DATEDIFF(day, @StartDate, @EndDate) +1
SELECT @ReducedDayswithEndDate = CASE DATENAME(weekday, @EndDate)
WHEN 'Saturday' THEN 1
WHEN 'Sunday' THEN 2
ELSE 0 END
SET @[email protected]@ReducedDayswithEndDate
SET @[email protected]/7;
SET @[email protected]%7;
SET @WorkDays=(@WeekPart*5)[email protected]
SELECT @WorkDays
Si va a utilizar una función, podría ser mejor ir con una función basada en la tabla como en [la respuesta de Mário Meyrelles] (http://stackoverflow.com/questions/252519/ count-work-days-between-two-dates/21098589 # 21098589) –
Mi versión de la respuesta aceptada como una función utilizando DATEPART
, así que no tengo que hacer una comparación de cadenas en la línea con
DATENAME(dw, @StartDate) = 'Sunday'
De todos modos, aquí está mi función datediff negocio
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
CREATE FUNCTION BDATEDIFF
(
@startdate as DATETIME,
@enddate as DATETIME
)
RETURNS INT
AS
BEGIN
DECLARE @res int
SET @res = (DATEDIFF(dd, @startdate, @enddate) + 1)
-(DATEDIFF(wk, @startdate, @enddate) * 2)
-(CASE WHEN DATEPART(dw, @startdate) = 1 THEN 1 ELSE 0 END)
-(CASE WHEN DATEPART(dw, @enddate) = 7 THEN 1 ELSE 0 END)
RETURN @res
END
GO
CREATE FUNCTION x
(
@StartDate DATETIME,
@EndDate DATETIME
)
RETURNS INT
AS
BEGIN
DECLARE @Teller INT
SET @StartDate = DATEADD(dd,1,@StartDate)
SET @Teller = 0
IF DATEDIFF(dd,@StartDate,@EndDate) <= 0
BEGIN
SET @Teller = 0
END
ELSE
BEGIN
WHILE
DATEDIFF(dd,@StartDate,@EndDate) >= 0
BEGIN
IF DATEPART(dw,@StartDate) < 6
BEGIN
SET @Teller = @Teller + 1
END
SET @StartDate = DATEADD(dd,1,@StartDate)
END
END
RETURN @Teller
END
Sería mejor si explicaras el código que publicaste. –
(yo soy un pocos puntos por debajo de los privilegios que comentan)
Si decide renunciar a la 1 día en CMS's elegant solution, tenga en cuenta que si su fecha de inicio y fecha de finalización son el mismo fin de semana, obtendrá una respuesta negativa. Es decir, 2008/10/26 a 2008/10/26 devuelve -1.
mi solución simplista:
select @Result = (..CMS's answer..)
if (@Result < 0)
select @Result = 0
RETURN @Result
.. que también establece todos los mensajes erróneos con fecha de inicio después de la fecha final, a cero. Algo que puede o no estar buscando.
Por diferencia entre las fechas incluyendo los días festivos que se fue de esta manera:
1) Tabla con las fiestas:
CREATE TABLE [dbo].[Holiday](
[Id] [int] IDENTITY(1,1) NOT NULL,
[Name] [nvarchar](50) NULL,
[Date] [datetime] NOT NULL)
2) tuve mi planificaciones Tabla así y quería llenar Work_Days columna que estaba vacío :
CREATE TABLE [dbo].[Plan_Phase](
[Id] [int] IDENTITY(1,1) NOT NULL,
[Id_Plan] [int] NOT NULL,
[Id_Phase] [int] NOT NULL,
[Start_Date] [datetime] NULL,
[End_Date] [datetime] NULL,
[Work_Days] [int] NULL)
3) por lo tanto, a fin de obtener "Work_Days" para llenar más tarde en mi columna sólo tenía que:
SELECT Start_Date, End_Date,
(DATEDIFF(dd, Start_Date, End_Date) + 1)
-(DATEDIFF(wk, Start_Date, End_Date) * 2)
-(SELECT COUNT(*) From Holiday Where Date >= Start_Date AND Date <= End_Date)
-(CASE WHEN DATENAME(dw, Start_Date) = 'Sunday' THEN 1 ELSE 0 END)
-(CASE WHEN DATENAME(dw, End_Date) = 'Saturday' THEN 1 ELSE 0 END)
-(CASE WHEN (SELECT COUNT(*) From Holiday Where Start_Date = Date) > 0 THEN 1 ELSE 0 END)
-(CASE WHEN (SELECT COUNT(*) From Holiday Where End_Date = Date) > 0 THEN 1 ELSE 0 END) AS Work_Days
from Plan_Phase
Espero que pueda ayudar.
Saludos
Con respecto a sus restas de vacaciones. ¿Qué sucede si la fecha de inicio es el 1 de enero y la fecha de finalización es el 31 de diciembre? Restarás solo 2, lo cual es incorrecto. Propongo usar DATEDIFF (día, fecha_inicio, fecha) y lo mismo para End_Date en lugar de todo 'SELECT COUNT (*) FROM Holiday ...'. –
Tomé los diversos ejemplos aquí, pero en mi situación particular tenemos un @PromisedDate para la entrega y una @ReceivedDate para la recepción real del artículo. Cuando se recibía un artículo antes del "PromisedDate", los cálculos no totalizaban correctamente a menos que ordenara que las fechas pasaran a la función por orden de calendario. No queriendo verificar las fechas cada vez, cambié la función para manejar esto por mí.
Create FUNCTION [dbo].[fnGetBusinessDays]
(
@PromiseDate date,
@ReceivedDate date
)
RETURNS integer
AS
BEGIN
DECLARE @days integer
SELECT @days =
Case when @PromiseDate > @ReceivedDate Then
DATEDIFF(d,@PromiseDate,@ReceivedDate) +
ABS(DATEDIFF(wk,@PromiseDate,@ReceivedDate)) * 2 +
CASE
WHEN DATENAME(dw, @PromiseDate) <> 'Saturday' AND DATENAME(dw, @ReceivedDate) = 'Saturday' THEN 1
WHEN DATENAME(dw, @PromiseDate) = 'Saturday' AND DATENAME(dw, @ReceivedDate) <> 'Saturday' THEN -1
ELSE 0
END +
(Select COUNT(*) FROM CompanyHolidays
WHERE HolidayDate BETWEEN @ReceivedDate AND @PromiseDate
AND DATENAME(dw, HolidayDate) <> 'Saturday' AND DATENAME(dw, HolidayDate) <> 'Sunday')
Else
DATEDIFF(d,@PromiseDate,@ReceivedDate) -
ABS(DATEDIFF(wk,@PromiseDate,@ReceivedDate)) * 2 -
CASE
WHEN DATENAME(dw, @PromiseDate) <> 'Saturday' AND DATENAME(dw, @ReceivedDate) = 'Saturday' THEN 1
WHEN DATENAME(dw, @PromiseDate) = 'Saturday' AND DATENAME(dw, @ReceivedDate) <> 'Saturday' THEN -1
ELSE 0
END -
(Select COUNT(*) FROM CompanyHolidays
WHERE HolidayDate BETWEEN @PromiseDate and @ReceivedDate
AND DATENAME(dw, HolidayDate) <> 'Saturday' AND DATENAME(dw, HolidayDate) <> 'Sunday')
End
RETURN (@days)
END
Aquí hay una versión que funciona bien (creo). La tabla de días festivos contiene las columnas de fechas de vacaciones que contienen las vacaciones que su empresa observa.
DECLARE @RAWDAYS INT
SELECT @RAWDAYS = DATEDIFF(day, @StartDate, @EndDate)--+1
-(2 * DATEDIFF(week, @StartDate, @EndDate))
+ CASE WHEN DATENAME(dw, @StartDate) = 'Saturday' THEN 1 ELSE 0 END
- CASE WHEN DATENAME(dw, @EndDate) = 'Saturday' THEN 1 ELSE 0 END
SELECT @RAWDAYS - COUNT(*)
FROM HOLIDAY NumberOfBusinessDays
WHERE [Holiday_Date] BETWEEN @StartDate+1 AND @EndDate
Esas fechas de vacaciones también pueden caer los fines de semana. Y para algunos, las vacaciones del domingo serán reemplazadas por el próximo lunes. –
todo el crédito a Bogdan Maxim & Peter Mortensen. Esta es su puesto, me acaba de agregar días de fiesta a la función (esto supone que tiene una tabla "tblHolidays" con un campo de fecha y hora "HolDate".
--Changing current database to the Master database allows function to be shared by everyone.
USE MASTER
GO
--If the function already exists, drop it.
IF EXISTS
(
SELECT *
FROM dbo.SYSOBJECTS
WHERE ID = OBJECT_ID(N'[dbo].[fn_WorkDays]')
AND XType IN (N'FN', N'IF', N'TF')
)
DROP FUNCTION [dbo].[fn_WorkDays]
GO
CREATE FUNCTION dbo.fn_WorkDays
--Presets
--Define the input parameters (OK if reversed by mistake).
(
@StartDate DATETIME,
@EndDate DATETIME = NULL [email protected] replaced by @StartDate when DEFAULTed
)
--Define the output data type.
RETURNS INT
AS
--Calculate the RETURN of the function.
BEGIN
--Declare local variables
--Temporarily holds @EndDate during date reversal.
DECLARE @Swap DATETIME
--If the Start Date is null, return a NULL and exit.
IF @StartDate IS NULL
RETURN NULL
--If the End Date is null, populate with Start Date value so will have two dates (required by DATEDIFF below).
IF @EndDate IS NULL
SELECT @EndDate = @StartDate
--Strip the time element from both dates (just to be safe) by converting to whole days and back to a date.
--Usually faster than CONVERT.
--0 is a date (01/01/1900 00:00:00.000)
SELECT @StartDate = DATEADD(dd,DATEDIFF(dd,0,@StartDate), 0),
@EndDate = DATEADD(dd,DATEDIFF(dd,0,@EndDate) , 0)
--If the inputs are in the wrong order, reverse them.
IF @StartDate > @EndDate
SELECT @Swap = @EndDate,
@EndDate = @StartDate,
@StartDate = @Swap
--Calculate and return the number of workdays using the input parameters.
--This is the meat of the function.
--This is really just one formula with a couple of parts that are listed on separate lines for documentation purposes.
RETURN (
SELECT
--Start with total number of days including weekends
(DATEDIFF(dd,@StartDate, @EndDate)+1)
--Subtact 2 days for each full weekend
-(DATEDIFF(wk,@StartDate, @EndDate)*2)
--If StartDate is a Sunday, Subtract 1
-(CASE WHEN DATENAME(dw, @StartDate) = 'Sunday'
THEN 1
ELSE 0
END)
--If EndDate is a Saturday, Subtract 1
-(CASE WHEN DATENAME(dw, @EndDate) = 'Saturday'
THEN 1
ELSE 0
END)
--Subtract all holidays
-(Select Count(*) from [DB04\DB04].[Gateway].[dbo].[tblHolidays]
where [HolDate] between @StartDate and @EndDate)
)
END
GO
-- Test Script
/*
declare @EndDate datetime= dateadd(m,2,getdate())
print @EndDate
select [Master].[dbo].[fn_WorkDays] (getdate(), @EndDate)
*/
Hola Dan B. Solo para hacerle saber que su versión asume que la tabla tblHolidays no contiene los sábados ni los lunes, lo que a veces ocurre. De todos modos, gracias por compartir tu versión. Cheers –
Julio - Sí - Mi versión asume que los sábados y domingos (no los lunes) son fines de semana, y por lo tanto no es un día "no comercial". Pero si trabajas los fines de semana, supongo que todos los días es un "día de trabajo" y puedes comentar la parte de la cláusula del sábado y domingo y simplemente agregar todas tus vacaciones a la mesa de tblHolidays. –
Si es necesario agregar días de trabajo a una fecha determinada, puede crear una función que depende de una tabla de calendario, se describen a continuación:
CREATE TABLE Calendar
(
dt SMALLDATETIME PRIMARY KEY,
IsWorkDay BIT
);
--fill the rows with normal days, weekends and holidays.
create function AddWorkingDays (@initialDate smalldatetime, @numberOfDays int)
returns smalldatetime as
begin
declare @result smalldatetime
set @result =
(
select t.dt from
(
select dt, ROW_NUMBER() over (order by dt) as daysAhead from calendar
where dt > @initialDate
and IsWorkDay = 1
) t
where t.daysAhead = @numberOfDays
)
return @result
end
+1 Terminé usando [una solución similar aquí] (http://stackoverflow.com/questions/32277191/sql-subquery-based-on-row-values-with-unrelated-table/32333303#32333303) –
Usando una tabla de fechas:
DECLARE
@StartDate date = '2014-01-01',
@EndDate date = '2014-01-31';
SELECT
COUNT(*) As NumberOfWeekDays
FROM dbo.Calendar
WHERE CalendarDate BETWEEN @StartDate AND @EndDate
AND IsWorkDay = 1;
Si no tienes eso, se puede utilizar una tabla de números:
DECLARE
@StartDate datetime = '2014-01-01',
@EndDate datetime = '2014-01-31';
SELECT
SUM(CASE WHEN DATEPART(dw, DATEADD(dd, Number-1, @StartDate)) BETWEEN 2 AND 6 THEN 1 ELSE 0 END) As NumberOfWeekDays
FROM dbo.Numbers
WHERE Number <= DATEDIFF(dd, @StartDate, @EndDate) + 1 -- Number table starts at 1, we want a 0 base
Deben ser rápidos y eliminar la ambigüedad/complejidad. La primera opción es la mejor, pero si no tienes una tabla de calendario, siempre puedes crear una tabla de números con un CTE.
Eso me funciona, en mi país los sábados y domingos son días inhábiles.
Para mí es importante el tiempo de @StartDate y @EndDate.
CREATE FUNCTION [dbo].[fnGetCountWorkingBusinessDays]
(
@StartDate as DATETIME,
@EndDate as DATETIME
)
RETURNS INT
AS
BEGIN
DECLARE @res int
SET @StartDate = CASE
WHEN DATENAME(dw, @StartDate) = 'Saturday' THEN DATEADD(dd, 2, DATEDIFF(dd, 0, @StartDate))
WHEN DATENAME(dw, @StartDate) = 'Sunday' THEN DATEADD(dd, 1, DATEDIFF(dd, 0, @StartDate))
ELSE @StartDate END
SET @EndDate = CASE
WHEN DATENAME(dw, @EndDate) = 'Saturday' THEN DATEADD(dd, 0, DATEDIFF(dd, 0, @EndDate))
WHEN DATENAME(dw, @EndDate) = 'Sunday' THEN DATEADD(dd, -1, DATEDIFF(dd, 0, @EndDate))
ELSE @EndDate END
SET @res =
(DATEDIFF(hour, @StartDate, @EndDate)/24)
- (DATEDIFF(wk, @StartDate, @EndDate) * 2)
SET @res = CASE WHEN @res < 0 THEN 0 ELSE @res END
RETURN @res
END
GO
Crear como:
CREATE FUNCTION dbo.fn_WorkDays(@StartDate DATETIME, @EndDate DATETIME= NULL)
RETURNS INT
AS
BEGIN
DECLARE @Days int
SET @Days = 0
IF @EndDate = NULL
SET @EndDate = EOMONTH(@StartDate) --last date of the month
WHILE DATEDIFF(dd,@StartDate,@EndDate) >= 0
BEGIN
IF DATENAME(dw, @StartDate) <> 'Saturday'
and DATENAME(dw, @StartDate) <> 'Sunday'
and Not ((Day(@StartDate) = 1 And Month(@StartDate) = 1)) --New Year's Day.
and Not ((Day(@StartDate) = 4 And Month(@StartDate) = 7)) --Independence Day.
BEGIN
SET @Days = @Days + 1
END
SET @StartDate = DATEADD(dd,1,@StartDate)
END
RETURN @Days
END
puede llamar a la función como:
select dbo.fn_WorkDays('1/1/2016', '9/25/2016')
O como:
select dbo.fn_WorkDays(StartDate, EndDate)
from table1
Esta es básicamente la respuesta de CMS sin la dependencia en una configuración de idioma particular. Y dado que estamos buscando genéricos, eso significa que también debería funcionar para todos los ajustes de @@datefirst
.
datediff(day, <start>, <end>) - datediff(week, <start>, <end>) * 2
/* if start is a Sunday, adjust by -1 */
+ case when datepart(weekday, <start>) = 8 - @@datefirst then -1 else 0 end
/* if end is a Saturday, adjust by -1 */
+ case when datepart(weekday, <end>) = (13 - @@datefirst) % 7 + 1 then -1 else 0 end
datediff(week, ...)
siempre utiliza un límite de sábado a domingo para semana, de modo que la expresión es determinista y no necesita ser modificado (siempre y cuando nuestra definición de lunes a viernes es siempre de lunes a viernes.) Día de numeración varía de acuerdo con la configuración @@datefirst
y los cálculos modificados manejan esta corrección con la pequeña complicación de alguna aritmética modular.
Una manera más limpia de lidiar con el sábado/domingo es traducir las fechas antes de extraer un valor del día de la semana. Después del cambio, los valores volverán a estar en línea con una numeración fija (y probablemente más familiar) que comienza con 1 el domingo y termina con 7 el sábado.
datediff(day, <start>, <end>) - datediff(week, <start>, <end>) * 2
+ case when datepart(weekday, dateadd(day, @@datefirst, <start>)) = 1 then -1 else 0 end,
+ case when datepart(weekday, dateadd(day, @@datefirst, <end>)) = 7 then -1 else 0 end
He rastreado esta forma de la solución remonta por lo menos tan lejos como 2002 y un artículo Itzik Ben-Gan. (https://technet.microsoft.com/en-us/library/aa175781(v=sql.80).aspx) Aunque necesitó una pequeña modificación ya que los nuevos tipos date
no permiten la aritmética de fecha, de lo contrario es idéntica.
Create Function dbo.DateDiff_WeekDays
(
@StartDate DateTime,
@EndDate DateTime
)
Returns Int
As
Begin
Declare @Result Int = 0
While @StartDate <= @EndDate
Begin
If DateName(DW, @StartDate) not in ('Saturday','Sunday')
Begin
Set @Result = @Result +1
End
Set @StartDate = DateAdd(Day, +1, @StartDate)
End
Return @Result
Fin
Otro enfoque para el cálculo de días de trabajo es utilizar un bucle while que básicamente itera a través de un rango de fechas y de incrementarlo en 1 cada vez que se encuentran día a estar dentro de lunes - viernes. El guión completo para el cálculo de días de trabajo utilizando el bucle while se muestra a continuación:
CREATE FUNCTION [dbo].[fn_GetTotalWorkingDaysUsingLoop]
(@DateFrom DATE,
@DateTo DATE
)
RETURNS INT
AS
BEGIN
DECLARE @TotWorkingDays INT= 0;
WHILE @DateFrom <= @DateTo
BEGIN
IF DATENAME(WEEKDAY, @DateFrom) IN('Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday')
BEGIN
SET @TotWorkingDays = @TotWorkingDays + 1;
END;
SET @DateFrom = DATEADD(DAY, 1, @DateFrom);
END;
RETURN @TotWorkingDays;
END;
GO
Aunque la opción de bucle WHILE es más limpio y utiliza menos líneas de código, que tiene el potencial de ser un cuello de botella en su entorno particular cuando su rango de fechas abarca varios años.
se puede ver más métodos en la forma de calcular los días de trabajo y las horas en este artículo: https://www.sqlshack.com/how-to-calculate-work-days-and-hours-in-sql-server/
me encontré con el siguiente TSQL una solución bastante elegante (no tengo permisos para ejecutar funciones). Encontré que el DATEDIFF
ignora DATEFIRST
y quería que mi primer día de la semana fuera un lunes. También quería que el primer día de trabajo se estableciera en cero y si se trata de un fin de semana el lunes será cero. Esto puede ayudar a alguien que tiene un requisito ligeramente diferente :)
No maneja festivos
SET DATEFIRST 1
SELECT
,(DATEDIFF(DD, [StartDate], [EndDate]))
-(DATEDIFF(wk, [StartDate], [EndDate]))
-(DATEDIFF(wk, DATEADD(dd,[email protected]@DATEFIRST,[StartDate]), DATEADD(dd,[email protected]@DATEFIRST,[EndDate]))) AS [WorkingDays]
FROM /*Your Table*/
Un enfoque consiste en 'caminar' las fechas de principio a fin en conjunción con una expresión case que comprueba si el día no es un sábado o un domingo y marcarlo (1 para el día de la semana, 0 para el fin de semana). Y al final solo suma banderas (sería igual al conteo de 1-banderas como la otra bandera es 0) para darte el número de días de la semana.
Puede utilizar un tipo de función de utilidad GetNums (número de inicio, número final) que genera una serie de números para 'bucle' desde la fecha de inicio hasta la fecha de finalización. Consulte http://tsql.solidq.com/SourceCodes/GetNums.txt para una implementación. La lógica también se puede ampliar para atender las vacaciones (por ejemplo, si tiene una tabla de vacaciones)
declare @date1 as datetime = '19900101'
declare @date2 as datetime = '19900120'
select sum(case when DATENAME(DW,currentDate) not in ('Saturday', 'Sunday') then 1 else 0 end) as noOfWorkDays
from dbo.GetNums(0,DATEDIFF(day,@date1, @date2)-1) as Num
cross apply (select DATEADD(day,n,@date1)) as Dates(currentDate)
¿Se pueden definir días laborables? cualquier lunes a viernes? ¿Excluyendo vacaciones importantes? ¿Que pais? Debe hacerse en SQL? –