Acabei de perceber que esse código nem sempre funciona! Eu tentei isso: SET @StartDate = '28 -mar-2011' SET @EndDate = '29 -mar-2011' a resposta contou-lo como 2 dias
greektreat
16
@greektreat Funciona bem. Só que @StartDate e @EndDate estão incluídos na contagem. Se você deseja que a segunda a terça-feira conte como 1 dia, remova o "+ 1" após o primeiro DATEDIFF. Então você também terá Fri-> Sat = 0, Fri-> Sun = 0, Fri-> Mon = 1.
Joe Daley
6
Como acompanhamento de @JoeDaley. Quando você remove o + 1 após o DATEDIFF para excluir a data de início da contagem, também é necessário ajustar a parte CASE. Acabei usando isso: + (CASE WHEN DATENAME (dw, @StartDate) = 'Saturday' THEN 1 ELSE 0 END) - (CASE WHEN DATENAME (dw, @EndDate) = 'Saturday' THEN 1 ELSE 0 END)
Sequenzia
7
A função de nome de dados depende da localidade. A solução mais robusta, mas também mais obscuro é substituir as duas últimas linhas 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 esclarecer o comentário de @ Sequenzia, você REMOVA inteiramente as declarações de caso sobre domingo, deixando apenas+(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
Em Calcular dias de trabalho, você pode encontrar um bom artigo sobre esse assunto, mas como pode ver, não é tão avançado.
--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
Se você precisar usar um calendário personalizado, poderá ser necessário adicionar algumas verificações e alguns parâmetros. Espero que isso forneça um bom ponto de partida.
Obrigado por incluir o link para entender como isso funciona. A gravação no sqlservercentral foi ótima!
31413 Chris Porter
20
Todo o crédito a Bogdan Maxim e Peter Mortensen. Esta é a postagem deles, acabei de adicionar feriados à função (Isso pressupõe que você tenha uma tabela "tblHolidays" com um campo de data e 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)
*/
Olá, Dan B. Apenas para informar que sua versão assume que a tabela tblHolidays não contém sábados e segundas-feiras, o que às vezes acontece. De qualquer forma, obrigado por compartilhar sua versão. Cheers
Julio Nobre
3
Julio - Sim - Minha versão pressupõe que sábado e domingo (não segunda-feira) são finais de semana e, portanto, não são dias "não comerciais". Mas se você estiver trabalhando nos fins de semana, acho que todo dia é um "dia útil" e você pode comentar a parte da cláusula de sábado e domingo e apenas adicionar todas as suas férias à tabela tblHolidays.
Dan B
1
Obrigado Dan. Eu incorporei isso em minha função, adicionando uma verificação para fins de semana, pois minha tabela DateDimensions inclui todas as datas, feriados, etc. Tomando sua função, eu apenas adicionei: e IsWeekend = 0 após o local [HolDate] entre StartDate e EndDate)
AlsoKnownAsJazz
Se a tabela Feriado contiver feriados nos finais de semana, você poderá alterar os critérios desta forma: WHERE HolDate BETWEEN @StartDate AND @EndDate AND DATEPART(dw, HolDate) BETWEEN 2 AND 6para contar apenas os feriados de segunda a sexta-feira.
Andre
7
Outra abordagem para calcular os dias úteis é usar um loop WHILE, que basicamente percorre um intervalo de datas e o incrementa em 1 sempre que os dias ocorrem entre segunda e sexta-feira. O script completo para calcular os dias úteis usando o loop WHILE é mostrado abaixo:
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
Embora a opção de loop WHILE seja mais limpa e use menos linhas de código, ela pode ser um gargalo de desempenho em seu ambiente, principalmente quando o período se estende por vários anos.
Minha versão da resposta aceita como uma função usando DATEPART, então não preciso fazer uma comparação de strings na linha com
DATENAME(dw,@StartDate)='Sunday'
Enfim, aqui está a minha função datada de negócios
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
Se você código postal, amostras de XML ou de dados, por favor destacar as linhas no editor de texto e clique no botão "código de amostras" ({}) na barra de ferramentas do editor de bem formato e sintaxe destacá-lo!
21411 Marc Marsh
Ótimo, não há necessidade de funções periféricas ou atualizações no banco de dados usando isso. Obrigado. Love the saltire btw :-)
Brian Scott
Super solução. Eu subbed em fórmulas para variáveis a serem usadas em um universo webi para calcular os dias da semana (MF) entre as datas em 2 colunas da tabela assim ... (((((DATEDIFF (dia, tabela.col1, tabela.col2) +1) - ((CASE DATENAME (dia da semana, tabela.col2) QUANDO 'Sábado' ENTÃO 1 QUANDO 'Domingo' ENTÃO 2 MAIS 0 FINAL))) / 7) * 5) + (((DATEDIFF (dia, tabela.col1, tabela.col2) ) +1) - ((CASE DATENAME (dia da semana, tabela.col2) QUANDO 'Sábado' ENTÃO 1 QUANDO 'Domingo' ENTÃO 2 MAIS 0 FIM)))% 7)
Hilary
5
(Eu tenho alguns pontos a menos de comentar os privilégios)
Se você decidir renunciar ao dia +1 na solução elegante do CMS , observe que, se sua data de início e data de término forem no mesmo fim de semana, você receberá uma resposta negativa. Ou seja, 2008/10/26 a 2008/10/26 retorna -1.
Em relação às subtrações de feriados. E se a data de início for 1º de janeiro e a data final for 31 de dezembro? Você subtrairá apenas 2 - o que está errado. Proponho usar DATEDIFF (dia, Data_Inicial, Data) e o mesmo para End_Date em vez de todo 'SELECT COUNT (*) FROM Holiday ...'.
Illia Ratkevych
4
Aqui está uma versão que funciona bem (eu acho). A tabela Holiday contém colunas Holiday_date que contêm feriados observados pela sua empresa.
Essas datas de feriado também podem cair nos finais de semana. E para alguns, as férias no domingo serão substituídas pela próxima segunda-feira.
Irawan Soetomo
3
Sei que essa é uma pergunta antiga, mas precisava de uma fórmula para dias úteis, excluindo a data de início, pois tenho vários itens e preciso que os dias se acumulem corretamente.
Nenhuma das respostas não iterativas funcionou para mim.
Eu usei uma definição como
Número de vezes que passa meia-noite a segunda-feira, terça-feira, quarta-feira, quinta-feira e sexta-feira
(outros podem contar da meia-noite ao sábado em vez de segunda-feira)
Aquele fez isso por mim, mas eu tive que fazer uma pequena alteração. Não estava explicando quando @StartDateé um sábado ou sexta-feira. Aqui está a minha versão: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 retorna 0, sábado a segunda-feira retorna 1, sexta-feira a sábado retorna 0. Todos são consistentes com minha definição. Seu código não irá acumular corretamente (por exemplo, retornar 6 para sexta-feira a sexta-feira, mas 5 para segunda-feira a segunda-feira)
adrianm
3
Essa é basicamente a resposta do CMS sem depender de uma configuração de idioma específica. E, como estamos filmando para genéricos, isso significa que deve funcionar para todas as @@datefirstconfigurações também.
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, ...) sempre usa um limite de sábado a domingo por semanas, para que a expressão seja determinística e não precise ser modificada (desde que nossa definição de dias da semana seja consistentemente de segunda a sexta-feira.) A numeração dos dias varia de acordo com o @@datefirst configuração e as cálculos modificados lidam com essa correção com a pequena complicação de alguma aritmética modular.
Uma maneira mais limpa de lidar com a coisa de sábado / domingo é traduzir as datas anteriores à extração do valor do dia da semana. Após a mudança, os valores voltarão à linha com uma numeração fixa (e provavelmente mais familiar) que começa com 1 no domingo e termina com 7 no sábado.
Rastreei essa forma da solução em pelo menos 2002 e em um artigo da Itzik Ben-Gan. ( https://technet.microsoft.com/en-us/library/aa175781(v=sql.80).aspx ) Apesar de precisar de um pequeno ajuste desde que o mais recentedate tipos não permitem aritmética de datas, é idêntico.
EDIT: Eu adicionei de volta o +1que de alguma forma foi deixado de fora. Também é importante notar que esse método sempre conta os dias de início e fim. Ele também pressupõe que a data de término seja posterior ou posterior à data de início.
Observe que isso retornará resultados incorretos para muitas datas nos fins de semana, para que não aumentem (Fri-> Seg deve ser o mesmo que Fri-> Sáb + Sáb-> Sol + Sol-> Seg). Fri-> Sáb deve ser 0 (correto), Sáb-> Dom deve ser 0 (errado -1), Dom-> Seg deve ser 1 (0 errado). Outros erros a seguir são Sat-> Sat = -1, Sun-> Sun = -1, Sun-> Sat = 4
adrianm
@adrianm Acho que corrigi os problemas. Na verdade, o problema era que ele sempre era desligado por uma, porque, de alguma forma, eu deixei essa parte por acidente.
precisa saber é o seguinte
Obrigado pela atualização. Eu pensei que sua fórmula estava excluindo a data de início, que é o que eu precisava. Resolvi e adicionei como outra resposta.
Adrianm 5/07
2
Usando uma tabela de datas:
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;
Se você não possui, pode usar uma tabela 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 devem ser rápidos e eliminam a ambiguidade / complexidade. A primeira opção é a melhor, mas se você não tiver uma tabela de calendário, poderá sempre criar uma tabela de números com um 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
Peguei os vários exemplos aqui, mas em minha situação particular, temos um @PromisedDate para entrega e um @ReceivedDate para o recebimento real do item. Quando um item foi recebido antes do "PromisedDate", os cálculos não estavam totalizando corretamente, a menos que eu pedisse as datas passadas para a função por ordem de calendário. Não querendo verificar as datas todas as vezes, mudei a função para lidar com isso para mim.
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
Se você precisar adicionar dias úteis a uma determinada data, poderá criar uma função que depende de uma tabela de calendário, descrita abaixo:
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
Assim como no DATEDIFF, não considero a data final como parte do intervalo. O número de (por exemplo) domingos entre @StartDate e @EndDate é o número de domingos entre uma segunda-feira "inicial" e o @EndDate menos o número de domingos entre essa segunda-feira "inicial" e o @StartDate. Sabendo disso, podemos calcular o número de dias úteis da seguinte maneira:
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
Eu achei o TSQL abaixo uma solução bastante elegante (não tenho permissões para executar funções). Eu encontrei os DATEDIFFignoraDATEFIRST e queria que meu primeiro dia da semana fosse segunda-feira. Eu também queria que o primeiro dia útil fosse definido como zero e, se cair em um fim de semana, a segunda-feira será zero. Isso pode ajudar alguém que tem um requisito ligeiramente diferente :)
Não lida com feriados
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*/
Uma abordagem é 'percorrer as datas' do início ao fim em conjunto com uma expressão de caso que verifica se o dia não é um sábado ou um domingo e sinalizá-lo (1 para dia da semana, 0 para final de semana). E, no final, basta somar sinalizadores (seria igual à contagem de 1 sinalizadores, pois o outro sinalizador é 0) para fornecer o número de dias da semana.
Você pode usar uma função de utilitário do tipo GetNums (startNumber, endNumber), que gera uma série de números para 'loop' da data de início até a data de término. Consulte http://tsql.solidq.com/SourceCodes/GetNums.txt para obter uma implementação. A lógica também pode ser estendida para atender a feriados (digamos se você tiver uma tabela de feriados)
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)
Peguei emprestadas algumas idéias de outras pessoas para criar minha solução. Eu uso o código embutido para ignorar fins de semana e feriados federais dos EUA. No meu ambiente, EndDate pode ser nulo, mas nunca precederá 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
Respostas:
Para dias úteis, de segunda a sexta-feira, você pode fazer isso com um único SELECT, assim:
Se você deseja incluir férias, você deve trabalhar um pouco ...
fonte
-(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)
Em Calcular dias de trabalho, você pode encontrar um bom artigo sobre esse assunto, mas como pode ver, não é tão avançado.
Se você precisar usar um calendário personalizado, poderá ser necessário adicionar algumas verificações e alguns parâmetros. Espero que isso forneça um bom ponto de partida.
fonte
Todo o crédito a Bogdan Maxim e Peter Mortensen. Esta é a postagem deles, acabei de adicionar feriados à função (Isso pressupõe que você tenha uma tabela "tblHolidays" com um campo de data e hora "HolDate".
fonte
WHERE HolDate BETWEEN @StartDate AND @EndDate AND DATEPART(dw, HolDate) BETWEEN 2 AND 6
para contar apenas os feriados de segunda a sexta-feira.Outra abordagem para calcular os dias úteis é usar um loop WHILE, que basicamente percorre um intervalo de datas e o incrementa em 1 sempre que os dias ocorrem entre segunda e sexta-feira. O script completo para calcular os dias úteis usando o loop WHILE é mostrado abaixo:
Embora a opção de loop WHILE seja mais limpa e use menos linhas de código, ela pode ser um gargalo de desempenho em seu ambiente, principalmente quando o período se estende por vários anos.
Você pode ver mais métodos sobre como calcular dias e horas de trabalho neste artigo: https://www.sqlshack.com/how-to-calculate-work-days-and-hours-in-sql-server/
fonte
Minha versão da resposta aceita como uma função usando
DATEPART
, então não preciso fazer uma comparação de strings na linha comEnfim, aqui está a minha função datada de negócios
fonte
fonte
(Eu tenho alguns pontos a menos de comentar os privilégios)
Se você decidir renunciar ao dia +1 na solução elegante do CMS , observe que, se sua data de início e data de término forem no mesmo fim de semana, você receberá uma resposta negativa. Ou seja, 2008/10/26 a 2008/10/26 retorna -1.
minha solução bastante simplista:
.. que também define todas as postagens incorretas com data de início após a data de término como zero. Algo que você pode ou não estar procurando.
fonte
Para a diferença entre datas, incluindo feriados, eu fui assim:
1) Mesa com feriados:
2) Eu tinha minha tabela de planejamentos assim e queria preencher a coluna Work_Days que estava vazia:
3) Então, para fazer com que "Work_Days" preencha posteriormente minha coluna, bastava:
Espero que eu possa ajudar.
Felicidades
fonte
Aqui está uma versão que funciona bem (eu acho). A tabela Holiday contém colunas Holiday_date que contêm feriados observados pela sua empresa.
fonte
Sei que essa é uma pergunta antiga, mas precisava de uma fórmula para dias úteis, excluindo a data de início, pois tenho vários itens e preciso que os dias se acumulem corretamente.
Nenhuma das respostas não iterativas funcionou para mim.
Eu usei uma definição como
(outros podem contar da meia-noite ao sábado em vez de segunda-feira)
Acabei com esta fórmula
fonte
@StartDate
é um sábado ou sexta-feira. Aqui está a minha versão: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
Essa é basicamente a resposta do CMS sem depender de uma configuração de idioma específica. E, como estamos filmando para genéricos, isso significa que deve funcionar para todas as
@@datefirst
configurações também.datediff(week, ...)
sempre usa um limite de sábado a domingo por semanas, para que a expressão seja determinística e não precise ser modificada (desde que nossa definição de dias da semana seja consistentemente de segunda a sexta-feira.) A numeração dos dias varia de acordo com o@@datefirst
configuração e as cálculos modificados lidam com essa correção com a pequena complicação de alguma aritmética modular.Uma maneira mais limpa de lidar com a coisa de sábado / domingo é traduzir as datas anteriores à extração do valor do dia da semana. Após a mudança, os valores voltarão à linha com uma numeração fixa (e provavelmente mais familiar) que começa com 1 no domingo e termina com 7 no sábado.
Rastreei essa forma da solução em pelo menos 2002 e em um artigo da Itzik Ben-Gan. ( https://technet.microsoft.com/en-us/library/aa175781(v=sql.80).aspx ) Apesar de precisar de um pequeno ajuste desde que o mais recente
date
tipos não permitem aritmética de datas, é idêntico.EDIT: Eu adicionei de volta o
+1
que de alguma forma foi deixado de fora. Também é importante notar que esse método sempre conta os dias de início e fim. Ele também pressupõe que a data de término seja posterior ou posterior à data de início.fonte
Usando uma tabela de datas:
Se você não possui, pode usar uma tabela de números:
Ambos devem ser rápidos e eliminam a ambiguidade / complexidade. A primeira opção é a melhor, mas se você não tiver uma tabela de calendário, poderá sempre criar uma tabela de números com um CTE.
fonte
fonte
fonte
Peguei os vários exemplos aqui, mas em minha situação particular, temos um @PromisedDate para entrega e um @ReceivedDate para o recebimento real do item. Quando um item foi recebido antes do "PromisedDate", os cálculos não estavam totalizando corretamente, a menos que eu pedisse as datas passadas para a função por ordem de calendário. Não querendo verificar as datas todas as vezes, mudei a função para lidar com isso para mim.
fonte
Se você precisar adicionar dias úteis a uma determinada data, poderá criar uma função que depende de uma tabela de calendário, descrita abaixo:
fonte
Assim como no DATEDIFF, não considero a data final como parte do intervalo. O número de (por exemplo) domingos entre @StartDate e @EndDate é o número de domingos entre uma segunda-feira "inicial" e o @EndDate menos o número de domingos entre essa segunda-feira "inicial" e o @StartDate. Sabendo disso, podemos calcular o número de dias úteis da seguinte maneira:
Cumprimentos!
fonte
Isso está funcionando para mim, no meu país, no sábado e domingo, são dias não úteis.
Para mim, é importante o tempo de @StartDate e @EndDate.
fonte
Crie uma função como:
Você pode chamar a função como:
Ou como:
fonte
Fim
fonte
Eu achei o TSQL abaixo uma solução bastante elegante (não tenho permissões para executar funções). Eu encontrei os
DATEDIFF
ignoraDATEFIRST
e queria que meu primeiro dia da semana fosse segunda-feira. Eu também queria que o primeiro dia útil fosse definido como zero e, se cair em um fim de semana, a segunda-feira será zero. Isso pode ajudar alguém que tem um requisito ligeiramente diferente :)Não lida com feriados
fonte
Uma abordagem é 'percorrer as datas' do início ao fim em conjunto com uma expressão de caso que verifica se o dia não é um sábado ou um domingo e sinalizá-lo (1 para dia da semana, 0 para final de semana). E, no final, basta somar sinalizadores (seria igual à contagem de 1 sinalizadores, pois o outro sinalizador é 0) para fornecer o número de dias da semana.
Você pode usar uma função de utilitário do tipo GetNums (startNumber, endNumber), que gera uma série de números para 'loop' da data de início até a data de término. Consulte http://tsql.solidq.com/SourceCodes/GetNums.txt para obter uma implementação. A lógica também pode ser estendida para atender a feriados (digamos se você tiver uma tabela de feriados)
fonte
Peguei emprestadas algumas idéias de outras pessoas para criar minha solução. Eu uso o código embutido para ignorar fins de semana e feriados federais dos EUA. No meu ambiente, EndDate pode ser nulo, mas nunca precederá StartDate.
fonte