¡Me acabo de dar cuenta de que este código no funciona siempre! probé esto: SET @StartDate = '28 -mar-2011 'SET @EndDate = '29 -mar-2011' la respuesta lo contó como 2 días
greektreat
16
@greektreat Funciona bien. Es solo que tanto @StartDate como @EndDate están incluidos en el conteo. Si desea que el lunes a martes cuente como 1 día, simplemente elimine el "+ 1" después del primer DATEDIFF. Entonces también obtendrás Fri-> Sat = 0, Fri-> Sun = 0, Fri-> Mon = 1.
Joe Daley el
66
Como seguimiento de @JoeDaley. Cuando elimina el + 1 después del DATEDIFF para excluir la fecha de inicio del recuento, también debe ajustar la parte CASO de esto. Terminé usando esto: + (CASO CUANDO DATENAME (dw, @StartDate) = 'Saturday' THEN 1 ELSE 0 END) - (CASE WHEN DATENAME (dw, @EndDate) = 'Saturday' THEN 1 ELSE 0 END)
Sequenzia
77
La función datename depende de la configuración regional. Una solución más robusta pero también más oscura es reemplazar las dos últimas líneas por:-(case datepart(dw, @StartDate)+@@datefirst when 8 then 1 else 0 end) -(case datepart(dw, @EndDate)+@@datefirst when 7 then 1 when 14 then 1 else 0 end)
Torben Klein
2
Para aclarar el comentario de @ Sequenzia, que eliminaría las declaraciones de casos sobre Domingo por completo, dejando sólo+(CASE WHEN DATENAME(dw, @StartDate) = 'Saturday' THEN 1 ELSE 0 END) - (CASE WHEN DATENAME(dw, @EndDate) = 'Saturday' THEN 1 ELSE 0 END)
Andy Raddatz
32
En Cálculo de días de trabajo puede encontrar un buen artículo sobre este tema, pero como puede ver, no es 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.IFEXISTS(SELECT*FROM dbo.SYSOBJECTS
WHERE ID = OBJECT_ID(N'[dbo].[fn_WorkDays]')AND XType IN(N'FN', N'IF', N'TF'))DROPFUNCTION[dbo].[fn_WorkDays]
GO
CREATEFUNCTION dbo.fn_WorkDays
--Presets--Define the input parameters (OK if reversed by mistake).(@StartDate DATETIME,@EndDate DATETIME =NULL--@EndDate 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 ISNULLRETURNNULL--If the End Date is null, populate with Start Date value so will have two dates (required by DATEDIFF below).IF@EndDate ISNULLSELECT@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-(CASEWHEN DATENAME(dw,@StartDate)='Sunday'THEN1ELSE0END)--If EndDate is a Saturday, Subtract 1-(CASEWHEN DATENAME(dw,@EndDate)='Saturday'THEN1ELSE0END))END
GO
Si necesita utilizar un calendario personalizado, es posible que deba agregar algunas comprobaciones y algunos parámetros. Esperemos que proporcione un buen punto de partida.
Gracias por incluir el enlace para entender cómo funciona esto. La escritura en sqlservercentral fue genial!
Chris Porter
20
Todo el crédito a Bogdan Maxim y Peter Mortensen. Esta es su publicación, acabo de agregar días festivos 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.IFEXISTS(SELECT*FROM dbo.SYSOBJECTS
WHERE ID = OBJECT_ID(N'[dbo].[fn_WorkDays]')AND XType IN(N'FN', N'IF', N'TF'))DROPFUNCTION[dbo].[fn_WorkDays]
GO
CREATEFUNCTION dbo.fn_WorkDays
--Presets--Define the input parameters (OK if reversed by mistake).(@StartDate DATETIME,@EndDate DATETIME =NULL--@EndDate 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 ISNULLRETURNNULL--If the End Date is null, populate with Start Date value so will have two dates (required by DATEDIFF below).IF@EndDate ISNULLSELECT@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-(CASEWHEN DATENAME(dw,@StartDate)='Sunday'THEN1ELSE0END)--If EndDate is a Saturday, Subtract 1-(CASEWHEN DATENAME(dw,@EndDate)='Saturday'THEN1ELSE0END)--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 y los lunes, lo que, a veces, sucede. De todos modos, gracias por compartir tu versión. Saludos
Julio Nobre
3
Julio - Sí - Mi versión asume que los sábados y domingos (no los lunes) son fines de semana y, por lo tanto, no son días "no hábiles". Pero si está trabajando los fines de semana, supongo que todos los días es un "día de trabajo" y puede comentar la parte de la cláusula los sábados y domingos y simplemente agregar todas sus vacaciones a la tabla tblHolidays.
Dan B
1
Gracias Dan. Lo incorporé a mi función, agregando un cheque para los fines de semana ya que mi tabla DateDimensions incluye todas las fechas, días festivos, etc. Tomando su función, acabo de agregar: y IsWeekend = 0 después de [HolDate] entre StartDate y EndDate)
AlsoKnownAsJazz
Si la tabla de días festivos contiene feriados los fines de semana, puede modificar los criterios de esta manera: WHERE HolDate BETWEEN @StartDate AND @EndDate AND DATEPART(dw, HolDate) BETWEEN 2 AND 6para contar solo los feriados de lunes a viernes.
Andre
7
Otro enfoque para calcular los días hábiles es usar un ciclo WHILE que básicamente itera a través de un rango de fechas y lo incrementa en 1 cada vez que se encuentran días dentro de lunes a viernes. La secuencia de comandos completa para calcular los días hábiles utilizando el ciclo WHILE se muestra a continuación:
CREATEFUNCTION[dbo].[fn_GetTotalWorkingDaysUsingLoop](@DateFrom DATE,@DateTo DATE
)
RETURNS INT
ASBEGINDECLARE@TotWorkingDays INT=0;WHILE@DateFrom <=@DateTo
BEGINIF DATENAME(WEEKDAY,@DateFrom)IN('Monday','Tuesday','Wednesday','Thursday','Friday')BEGINSET@TotWorkingDays =@TotWorkingDays +1;END;SET@DateFrom = DATEADD(DAY,1,@DateFrom);END;RETURN@TotWorkingDays;END;
GO
Aunque la opción WHILE loop es más limpia y usa menos líneas de código, tiene el potencial de ser un cuello de botella de rendimiento en su entorno, particularmente cuando su rango de fechas se extiende a lo largo de varios años.
Mi versión de la respuesta aceptada como una función usando DATEPART, por lo 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 dataiff de negocios
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
CREATEFUNCTION BDATEDIFF
(@startdate as DATETIME,@enddate as DATETIME
)
RETURNS INT
ASBEGINDECLARE@res int
SET@res =(DATEDIFF(dd,@startdate,@enddate)+1)-(DATEDIFF(wk,@startdate,@enddate)*2)-(CASEWHEN DATEPART(dw,@startdate)=1THEN1ELSE0END)-(CASEWHEN DATEPART(dw,@enddate)=7THEN1ELSE0END)RETURN@res
END
GO
DECLARE@TotalDays INT,@WorkDays INT
DECLARE@ReducedDayswithEndDate INT
DECLARE@WeekPart INT
DECLARE@DatePart INT
SET@TotalDays= DATEDIFF(day,@StartDate,@EndDate)+1SELECT@ReducedDayswithEndDate =CASE DATENAME(weekday,@EndDate)WHEN'Saturday'THEN1WHEN'Sunday'THEN2ELSE0ENDSET@TotalDays=@TotalDays-@ReducedDayswithEndDate
SET@WeekPart=@TotalDays/7;SET@DatePart=@TotalDays%7;SET@WorkDays=(@WeekPart*5)+@DatePart
RETURN@WorkDays
Si el código de poste, XML o datos de muestras, por favor, destacar aquellas líneas en el editor de texto y haga clic en el botón "muestras de código" ({}) en la barra de herramientas de editor de formato y la sintaxis muy bien resáltala!
marc_s
Genial, sin necesidad de funciones periféricas o actualizaciones de la base de datos usando esto. Gracias. Love the saltire por cierto :-)
Brian Scott
Súper solución. Subtitulé en fórmulas las variables para usar en un universo webi para calcular los días de la semana (MF) entre las fechas en 2 columnas de la tabla de esta manera ... (((((DATEDIFF (day, table.col1, table.col2) +1) - ((CASO DATENAME (día de la semana, table.col2) CUANDO 'Sábado' ENTONCES 1 CUANDO 'Domingo' ENTONCES 2 OTRO 0 FIN))) / 7) * 5) + (((DATEDIFF (día, table.col1, table.col2 ) +1) - ((CASO DATENAME (día de la semana, table.col2) CUANDO 'Sábado' ENTONCES 1 CUANDO 'Domingo' ENTONCES 2 ELSE 0 FIN)))% 7)
Hilary
5
(Me faltan algunos puntos para comentar los privilegios)
Si decide renunciar al día +1 en la elegante solución de CMS , tenga en cuenta que si su fecha de inicio y finalización son el mismo fin de semana, obtendrá una respuesta negativa. Es decir, del 26/10/2008 al 26/10/2008 devuelve -1.
.. que también establece todas las publicaciones erróneas con fecha de inicio después de la fecha de finalización en cero. Algo que puede o no estar buscando.
En cuanto a sus sustracciones de vacaciones. ¿Qué pasa si la fecha de inicio es el 1 de enero y la fecha de finalización es el 31 de diciembre? Restará solo 2, lo cual está mal. Propongo usar DATEDIFF (día, fecha_inicio, fecha) y lo mismo para fecha_final en lugar de todo 'SELECCIONAR CUENTA (*) DE vacaciones ...'.
Illia Ratkevych
4
Aquí hay una versión que funciona bien (creo). La tabla de vacaciones contiene columnas Holiday_date que contienen vacaciones que observa su empresa.
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.
Irawan Soetomo
3
Sé que esta es una pregunta antigua, pero necesitaba una fórmula para los días de trabajo, excluyendo la fecha de inicio, ya que tengo varios elementos y necesito que los días se acumulen correctamente.
Ninguna de las respuestas no iterativas funcionó para mí.
Usé una definición como
Número de veces que pasa de medianoche a lunes, martes, miércoles, jueves y viernes
(otros pueden contar de medianoche a sábado en lugar de lunes)
Ese lo hizo por mí, pero tuve que hacer un pequeño cambio. No contaba cuándo @StartDatees un sábado o viernes. Aquí está mi versión:DATEDIFF(day, @StartDate, @EndDate) - DATEDIFF(week, @StartDate, @EndDate) - DATEDIFF(week, DATEADD(day, 1, @StartDate), DATEADD(day, 1, @EndDate)) - (CASE WHEN DATEPART(WEEKDAY, @StartDate) IN (1, 7) THEN 1 ELSE 0 END) + 1
caiosm1005
@ caiosm1005, sábado a domingo devuelve 0, sábado a lunes devuelve 1, viernes a sábado devuelve 0. Todos son consistentes con mi definición. Su código no se acumulará correctamente (por ejemplo, devuelva 6 de viernes a viernes pero 5 de lunes a lunes)
adrianm
3
Esta es básicamente la respuesta de CMS sin depender de una configuración de idioma particular. Y dado que estamos buscando genéricos, eso significa que también debería funcionar para todas las @@datefirstconfiguraciones.
datediff(day,<start>,<end>)+1- datediff(week,<start>,<end>)*2/* if start is a Sunday, adjust by -1 */+casewhen datepart(weekday,<start>)=8-@@datefirst then-1else0end/* if end is a Saturday, adjust by -1 */+casewhen datepart(weekday,<end>)=(13-@@datefirst)%7+1then-1else0end
datediff(week, ...) siempre usa un límite de sábado a domingo durante semanas, por lo que la expresión es determinista y no necesita ser modificada (siempre y cuando nuestra definición de días de la semana sea consistentemente de lunes a viernes). La numeración de los días varía según el @@datefirst configuración y Los cálculos modificados manejan esta corrección con la pequeña complicación de alguna aritmética modular.
Una forma más limpia de lidiar con lo del sábado / domingo es traducir las fechas antes de extraer el valor de un 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.
He rastreado esta forma de solución al menos hasta 2002 y un artículo de Itzik Ben-Gan. ( https://technet.microsoft.com/en-us/library/aa175781(v=sql.80).aspx ) Aunque necesitaba un pequeño ajuste ya que los datetipos más nuevos no permiten la aritmética de fechas, de lo contrario es idéntico.
EDITAR: agregué de nuevo el +1que de alguna manera se había dejado. También vale la pena señalar que este método siempre cuenta los días de inicio y finalización. También supone que la fecha de finalización es igual o posterior a la fecha de inicio.
Tenga en cuenta que esto devolverá resultados incorrectos para muchas fechas los fines de semana para que no sumen p (Fri-> Mon debería ser igual que Fri-> Sat + Sat-> Sun + Sun-> Mon). Fri-> Sat debería ser 0 (correcto), Sat-> Sun debería ser 0 (incorrecto -1), Sun-> Mon debería ser 1 (incorrecto 0). Otros errores que siguen son Sat-> Sat = -1, Sun-> Sun = -1, Sun-> Sat = 4
adrianm el
@adrianm Creo que había corregido los problemas. En realidad, el problema era que siempre estaba apagado por uno porque de alguna manera había dejado caer esa parte por accidente.
shawnt00
Gracias por la actualización. Pensé que su fórmula excluía la fecha de inicio, que es lo que necesitaba. Lo resolví yo mismo y lo agregué como otra respuesta.
adrianm
2
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 tiene eso, puede usar una tabla de números:
DECLARE@StartDate datetime ='2014-01-01',@EndDate datetime ='2014-01-31';SELECT
SUM(CASEWHEN DATEPART(dw, DATEADD(dd, Number-1,@StartDate))BETWEEN2AND6THEN1ELSE0END)As NumberOfWeekDays
FROM dbo.Numbers
WHERE Number <= DATEDIFF(dd,@StartDate,@EndDate)+1-- Number table starts at 1, we want a 0 base
Ambos deben ser rápidos y elimina la ambigüedad / complejidad. La primera opción es la mejor, pero si no tiene una tabla de calendario, siempre puede crear una tabla de números con un CTE.
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)+1SELECT@ReducedDayswithEndDate =CASE DATENAME(weekday,@EndDate)WHEN'Saturday'THEN1WHEN'Sunday'THEN2ELSE0ENDSET@TotalDays=@TotalDays-@ReducedDayswithEndDate
SET@WeekPart=@TotalDays/7;SET@DatePart=@TotalDays%7;SET@WorkDays=(@WeekPart*5)+@DatePart
SELECT@WorkDays
Tomé los diversos ejemplos aquí, pero en mi situación particular tenemos un @PromisedDate para la entrega y un @ReceivedDate para la recepción real del artículo. Cuando se recibió un artículo antes del "PromisedDate", los cálculos no totalizaban correctamente a menos que ordenara las fechas pasadas a la función por orden de calendario. No queriendo verificar las fechas cada vez, cambié la función para manejar esto por mí.
CreateFUNCTION[dbo].[fnGetBusinessDays](@PromiseDate date,@ReceivedDate date
)
RETURNS integer
ASBEGINDECLARE@days integer
SELECT@days =Casewhen@PromiseDate >@ReceivedDate Then
DATEDIFF(d,@PromiseDate,@ReceivedDate)+
ABS(DATEDIFF(wk,@PromiseDate,@ReceivedDate))*2+CASEWHEN DATENAME(dw,@PromiseDate)<>'Saturday'AND DATENAME(dw,@ReceivedDate)='Saturday'THEN1WHEN DATENAME(dw,@PromiseDate)='Saturday'AND DATENAME(dw,@ReceivedDate)<>'Saturday'THEN-1ELSE0END+(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-CASEWHEN DATENAME(dw,@PromiseDate)<>'Saturday'AND DATENAME(dw,@ReceivedDate)='Saturday'THEN1WHEN DATENAME(dw,@PromiseDate)='Saturday'AND DATENAME(dw,@ReceivedDate)<>'Saturday'THEN-1ELSE0END-(Select COUNT(*)FROM CompanyHolidays
WHERE HolidayDate BETWEEN@PromiseDate and@ReceivedDate
AND DATENAME(dw, HolidayDate)<>'Saturday'AND DATENAME(dw, HolidayDate)<>'Sunday')EndRETURN(@days)END
Si necesita agregar días hábiles a una fecha determinada, puede crear una función que dependa de una tabla de calendario, que se describe a continuación:
CREATETABLE Calendar
(
dt SMALLDATETIME PRIMARYKEY,
IsWorkDay BIT
);--fill the rows with normal days, weekends and holidays.createfunction AddWorkingDays (@initialDate smalldatetime,@numberOfDays int)
returns smalldatetime asbegindeclare@result smalldatetime
set@result =(select t.dt from(select dt, ROW_NUMBER()over(orderby dt)as daysAhead from calendar
where dt >@initialDate
and IsWorkDay =1) t
where t.daysAhead =@numberOfDays
)return@result
end
Al igual que con DATEDIFF, no considero que la fecha de finalización sea parte del intervalo. El número de (por ejemplo) domingos entre @StartDate y @EndDate es el número de domingos entre un lunes "inicial" y el @EndDate menos el número de domingos entre este lunes "inicial" y el @StartDate. Sabiendo esto, podemos calcular el número de días laborables de la siguiente manera:
CREATEFUNCTION dbo.fn_WorkDays(@StartDate DATETIME,@EndDate DATETIME=NULL)
RETURNS INT
ASBEGINDECLARE@Days int
SET@Days =0IF@EndDate =NULLSET@EndDate = EOMONTH(@StartDate)--last date of the monthWHILE DATEDIFF(dd,@StartDate,@EndDate)>=0BEGINIF DATENAME(dw,@StartDate)<>'Saturday'and DATENAME(dw,@StartDate)<>'Sunday'andNot((Day(@StartDate)=1And Month(@StartDate)=1))--New Year's Day.andNot((Day(@StartDate)=4And Month(@StartDate)=7))--Independence Day.BEGINSET@Days =@Days +1ENDSET@StartDate = DATEADD(dd,1,@StartDate)ENDRETURN@Days
END
Encontré el siguiente TSQL como una solución bastante elegante (no tengo permisos para ejecutar funciones). Encontré los DATEDIFFignoraDATEFIRST y quería que mi primer día de la semana fuera lunes. También quería que el primer día hábil se estableciera en cero y si cae un fin de semana, el lunes será un cero. Esto puede ayudar a alguien que tiene un requisito ligeramente diferente :)
No maneja feriados bancarios
SET DATEFIRST 1SELECT,(DATEDIFF(DD,[StartDate],[EndDate]))-(DATEDIFF(wk,[StartDate],[EndDate]))-(DATEDIFF(wk, DATEADD(dd,-@@DATEFIRST,[StartDate]), DATEADD(dd,-@@DATEFIRST,[EndDate])))AS[WorkingDays]FROM/*Your Table*/
Un enfoque es 'recorrer las fechas' de principio a fin junto con una expresión de caso que verifica si el día no es sábado o domingo y lo marca (1 para el día de la semana, 0 para el fin de semana). Y al final solo suma banderas (sería igual a la cuenta de 1 banderas ya que la otra bandera es 0) para darte la cantidad de días de la semana.
Puede usar un tipo de función de utilidad GetNums (startNumber, endNumber) que genera una serie de números para 'bucles' 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(casewhen DATENAME(DW,currentDate)notin('Saturday','Sunday')then1else0end)as noOfWorkDays
from dbo.GetNums(0,DATEDIFF(day,@date1,@date2)-1)as Num
crossapply(select DATEADD(day,n,@date1))as Dates(currentDate)
Tomé prestadas algunas ideas de otros para crear mi solución. Utilizo el código en línea para ignorar los fines de semana y feriados federales de los EE. UU. En mi entorno, EndDate puede ser nulo, pero nunca precederá a StartDate.
CREATEFUNCTION dbo.ufn_CalculateBusinessDays(@StartDate DATE,@EndDate DATE =NULL)
RETURNS INT
ASBEGINDECLARE@TotalBusinessDays INT =0;DECLARE@TestDate DATE =@StartDate;IF@EndDate ISNULLRETURNNULL;WHILE@TestDate <@EndDate
BEGINDECLARE@Month INT = DATEPART(MM,@TestDate);DECLARE@Day INT = DATEPART(DD,@TestDate);DECLARE@DayOfWeek INT = DATEPART(WEEKDAY,@TestDate)-1;--Monday = 1, Tuesday = 2, etc.DECLARE@DayOccurrence INT =(@Day -1)/7+1;--Nth day of month (3rd Monday, for example)--Increment business day counter if not a weekend or holidaySELECT@TotalBusinessDays +=(SELECTCASE--Saturday OR SundayWHEN@DayOfWeek IN(6,7)THEN0--New Year's DayWHEN@Month =1AND@Day =1THEN0--MLK Jr. DayWHEN@Month =1AND@DayOfWeek =1AND@DayOccurrence =3THEN0--G. Washington's BirthdayWHEN@Month =2AND@DayOfWeek =1AND@DayOccurrence =3THEN0--Memorial DayWHEN@Month =5AND@DayOfWeek =1AND@Day BETWEEN25AND31THEN0--Independence DayWHEN@Month =7AND@Day =4THEN0--Labor DayWHEN@Month =9AND@DayOfWeek =1AND@DayOccurrence =1THEN0--Columbus DayWHEN@Month =10AND@DayOfWeek =1AND@DayOccurrence =2THEN0--Veterans DayWHEN@Month =11AND@Day =11THEN0--ThanksgivingWHEN@Month =11AND@DayOfWeek =4AND@DayOccurrence =4THEN0--ChristmasWHEN@Month =12AND@Day =25THEN0ELSE1ENDAS Result);SET@TestDate = DATEADD(dd,1,@TestDate);ENDRETURN@TotalBusinessDays;END
Respuestas:
Para los días laborables, de lunes a viernes, puede hacerlo con un solo SELECT, como este:
Si desea incluir vacaciones, debe resolverlo un poco ...
fuente
-(case datepart(dw, @StartDate)+@@datefirst when 8 then 1 else 0 end) -(case datepart(dw, @EndDate)+@@datefirst when 7 then 1 when 14 then 1 else 0 end)
+(CASE WHEN DATENAME(dw, @StartDate) = 'Saturday' THEN 1 ELSE 0 END) - (CASE WHEN DATENAME(dw, @EndDate) = 'Saturday' THEN 1 ELSE 0 END)
En Cálculo de días de trabajo puede encontrar un buen artículo sobre este tema, pero como puede ver, no es tan avanzado.
Si necesita utilizar un calendario personalizado, es posible que deba agregar algunas comprobaciones y algunos parámetros. Esperemos que proporcione un buen punto de partida.
fuente
Todo el crédito a Bogdan Maxim y Peter Mortensen. Esta es su publicación, acabo de agregar días festivos a la función (Esto supone que tiene una tabla "tblHolidays" con un campo de fecha y hora "HolDate".
fuente
WHERE HolDate BETWEEN @StartDate AND @EndDate AND DATEPART(dw, HolDate) BETWEEN 2 AND 6
para contar solo los feriados de lunes a viernes.Otro enfoque para calcular los días hábiles es usar un ciclo WHILE que básicamente itera a través de un rango de fechas y lo incrementa en 1 cada vez que se encuentran días dentro de lunes a viernes. La secuencia de comandos completa para calcular los días hábiles utilizando el ciclo WHILE se muestra a continuación:
Aunque la opción WHILE loop es más limpia y usa menos líneas de código, tiene el potencial de ser un cuello de botella de rendimiento en su entorno, particularmente cuando su rango de fechas se extiende a lo largo de varios años.
Puede ver más métodos sobre cómo calcular días y horas de trabajo en este artículo: https://www.sqlshack.com/how-to-calculate-work-days-and-hours-in-sql-server/
fuente
Mi versión de la respuesta aceptada como una función usando
DATEPART
, por lo que no tengo que hacer una comparación de cadenas en la línea conDe todos modos, aquí está mi función dataiff de negocios
fuente
fuente
(Me faltan algunos puntos para comentar los privilegios)
Si decide renunciar al día +1 en la elegante solución de CMS , tenga en cuenta que si su fecha de inicio y finalización son el mismo fin de semana, obtendrá una respuesta negativa. Es decir, del 26/10/2008 al 26/10/2008 devuelve -1.
mi solución bastante simplista:
.. que también establece todas las publicaciones erróneas con fecha de inicio después de la fecha de finalización en cero. Algo que puede o no estar buscando.
fuente
Por la diferencia entre las fechas, incluidas las vacaciones, fui de esta manera:
1) Mesa con vacaciones:
2) Tenía mi tabla de planificación como esta y quería llenar la columna Work_Days que estaba vacía:
3) Entonces, para que "Work_Days" complete más tarde mi columna solo tenía que:
Espero que pueda ayudar.
Salud
fuente
Aquí hay una versión que funciona bien (creo). La tabla de vacaciones contiene columnas Holiday_date que contienen vacaciones que observa su empresa.
fuente
Sé que esta es una pregunta antigua, pero necesitaba una fórmula para los días de trabajo, excluyendo la fecha de inicio, ya que tengo varios elementos y necesito que los días se acumulen correctamente.
Ninguna de las respuestas no iterativas funcionó para mí.
Usé una definición como
(otros pueden contar de medianoche a sábado en lugar de lunes)
Terminé con esta fórmula
fuente
@StartDate
es un sábado o viernes. Aquí está mi versión:DATEDIFF(day, @StartDate, @EndDate) - DATEDIFF(week, @StartDate, @EndDate) - DATEDIFF(week, DATEADD(day, 1, @StartDate), DATEADD(day, 1, @EndDate)) - (CASE WHEN DATEPART(WEEKDAY, @StartDate) IN (1, 7) THEN 1 ELSE 0 END) + 1
Esta es básicamente la respuesta de CMS sin depender de una configuración de idioma particular. Y dado que estamos buscando genéricos, eso significa que también debería funcionar para todas las
@@datefirst
configuraciones.datediff(week, ...)
siempre usa un límite de sábado a domingo durante semanas, por lo que la expresión es determinista y no necesita ser modificada (siempre y cuando nuestra definición de días de la semana sea consistentemente de lunes a viernes). La numeración de los días varía según el@@datefirst
configuración y Los cálculos modificados manejan esta corrección con la pequeña complicación de alguna aritmética modular.Una forma más limpia de lidiar con lo del sábado / domingo es traducir las fechas antes de extraer el valor de un 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.
He rastreado esta forma de solución al menos hasta 2002 y un artículo de Itzik Ben-Gan. ( https://technet.microsoft.com/en-us/library/aa175781(v=sql.80).aspx ) Aunque necesitaba un pequeño ajuste ya que los
date
tipos más nuevos no permiten la aritmética de fechas, de lo contrario es idéntico.EDITAR: agregué de nuevo el
+1
que de alguna manera se había dejado. También vale la pena señalar que este método siempre cuenta los días de inicio y finalización. También supone que la fecha de finalización es igual o posterior a la fecha de inicio.fuente
Usando una tabla de fechas:
Si no tiene eso, puede usar una tabla de números:
Ambos deben ser rápidos y elimina la ambigüedad / complejidad. La primera opción es la mejor, pero si no tiene una tabla de calendario, siempre puede crear una tabla de números con un CTE.
fuente
fuente
fuente
Tomé los diversos ejemplos aquí, pero en mi situación particular tenemos un @PromisedDate para la entrega y un @ReceivedDate para la recepción real del artículo. Cuando se recibió un artículo antes del "PromisedDate", los cálculos no totalizaban correctamente a menos que ordenara las fechas pasadas a la función por orden de calendario. No queriendo verificar las fechas cada vez, cambié la función para manejar esto por mí.
fuente
Si necesita agregar días hábiles a una fecha determinada, puede crear una función que dependa de una tabla de calendario, que se describe a continuación:
fuente
Al igual que con DATEDIFF, no considero que la fecha de finalización sea parte del intervalo. El número de (por ejemplo) domingos entre @StartDate y @EndDate es el número de domingos entre un lunes "inicial" y el @EndDate menos el número de domingos entre este lunes "inicial" y el @StartDate. Sabiendo esto, podemos calcular el número de días laborables de la siguiente manera:
¡Atentamente!
fuente
Eso funciona para mí, en mi país los sábados y domingos son días no laborables.
Para mí es importante el tiempo de @StartDate y @EndDate.
fuente
Crear función como:
Puede llamar a la función como:
O como:
fuente
Final
fuente
Encontré el siguiente TSQL como una solución bastante elegante (no tengo permisos para ejecutar funciones). Encontré los
DATEDIFF
ignoraDATEFIRST
y quería que mi primer día de la semana fuera lunes. También quería que el primer día hábil se estableciera en cero y si cae un fin de semana, el lunes será un cero. Esto puede ayudar a alguien que tiene un requisito ligeramente diferente :)No maneja feriados bancarios
fuente
Un enfoque es 'recorrer las fechas' de principio a fin junto con una expresión de caso que verifica si el día no es sábado o domingo y lo marca (1 para el día de la semana, 0 para el fin de semana). Y al final solo suma banderas (sería igual a la cuenta de 1 banderas ya que la otra bandera es 0) para darte la cantidad de días de la semana.
Puede usar un tipo de función de utilidad GetNums (startNumber, endNumber) que genera una serie de números para 'bucles' 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)
fuente
Tomé prestadas algunas ideas de otros para crear mi solución. Utilizo el código en línea para ignorar los fines de semana y feriados federales de los EE. UU. En mi entorno, EndDate puede ser nulo, pero nunca precederá a StartDate.
fuente