operaciones meses mes fechas entre ejemplos dias con calcular años sql tsql date

sql - meses - Cuente los días de trabajo entre dos fechas



dias del mes sql (20)

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

¿Cómo puedo calcular el número de días de trabajo entre dos fechas en SQL Server?

De lunes a viernes y debe ser T-SQL.


Crear una función 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


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. 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 --@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 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 deba agregar algunas verificaciones y algunos parámetros. Con suerte, proporcionará un buen punto de partida.


Encontré 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 feriados bancarios

SET DATEFIRST 1 SELECT ,(DATEDIFF(DD, [StartDate], [EndDate])) -(DATEDIFF(wk, [StartDate], [EndDate])) -(DATEDIFF(wk, DATEADD(dd,-@@DATEFIRST,[StartDate]), DATEADD(dd,-@@DATEFIRST,[EndDate]))) AS [WorkingDays] FROM /*Your Table*/


Eso está funcionando para mí, en mi país los sábados y domingos son días no laborables.

Para mí es importante el momento 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


Esta es básicamente la respuesta de CMS sin la dependencia 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 configuraciones 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 usa un límite de sábado a domingo durante semanas, por lo que la expresión es determinista y no necesita modificación (siempre que nuestra definición de días laborables sea consistentemente de lunes a viernes). Numeración de días 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 una aritmética modular.

Una forma más limpia de lidiar con el asunto del sábado / domingo es traducir las fechas antes de extraer el 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 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 una pequeña modificación, ya que los tipos de date más nuevos no permiten la aritmética de fecha, de lo contrario es idéntica.


Mi versión de la respuesta aceptada como una función usando 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 de fecha de negocios

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


Otro método para calcular los días hábiles es usar un ciclo WHILE que básicamente itera a través de un rango de fechas e incrementarlo en 1 cada vez que los días se encuentren dentro de lunes a viernes. La secuencia de comandos completa para calcular los días de trabajo utilizando el ciclo 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 ciclo WHILE 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 abarca 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/


Para días laborables, de lunes a viernes, puede hacerlo con un SELECT único, como este:

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 vacaciones, debe resolverlo un poco ...


Para la diferencia entre las fechas, incluidas las vacaciones, fui de esta manera:

1) Tabla con vacaciones:

CREATE TABLE [dbo].[Holiday]( [Id] [int] IDENTITY(1,1) NOT NULL, [Name] [nvarchar](50) NULL, [Date] [datetime] NOT NULL)

2) Tenía mi plan de planificación de esta manera y quería completar la columna Días de trabajo que estaba vacía:

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) Entonces, para obtener "Días de trabajo" para luego completar mi columna solo 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 poder ayudar.

Aclamaciones


Si necesita agregar días de trabajo a una fecha determinada, puede crear una función que depende de una tabla de calendario, que se describe 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


Todo el crédito a Bogdan Maxim y Peter Mortensen. Esta es su publicación, acabo de agregar vacaciones 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 --@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 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) */


Tomé varios 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 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


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 un sábado o un 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 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 (startNumber, endNumber) 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 extender para atender las vacaciones (por ejemplo, si tiene una mesa 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)


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(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.


(Estoy a unos pocos puntos de 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 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 bastante simplista:

select @Result = (..CMS''s answer..) if (@Result < 0) select @Result = 0 RETURN @Result

.. que también establece todas las publicaciones erróneas con la fecha de inicio después de la fecha de finalización en cero. Algo que puede o no estar buscando.


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 @TotalDays=@TotalDays-@ReducedDayswithEndDate SET @WeekPart=@TotalDays/7; SET @DatePart=@TotalDays%7; SET @WorkDays=(@WeekPart*5)+@DatePart RETURN @WorkDays


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


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


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 @TotalDays=@TotalDays-@ReducedDayswithEndDate SET @WeekPart=@TotalDays/7; SET @DatePart=@TotalDays%7; SET @WorkDays=(@WeekPart*5)+@DatePart SELECT @WorkDays