Je viens de réaliser que ce code ne fonctionne pas toujours! J'ai essayé ceci: SET @StartDate = '28 -mar-2011 'SET @EndDate = '29 -mar-2011' la réponse l'a compté comme 2 jours
greektreat
16
@greektreat Cela fonctionne bien. C'est juste que @StartDate et @EndDate sont inclus dans le décompte. Si vous voulez que du lundi au mardi compte pour 1 jour, supprimez simplement le "+ 1" après le premier DATEDIFF. Ensuite, vous obtiendrez également ven-> sam = 0, ven-> dim = 0, ven-> lun = 1.
Joe Daley
6
Dans le prolongement de @JoeDaley. Lorsque vous supprimez le + 1 après le DATEDIFF pour exclure la date de début du décompte, vous devez également ajuster la partie CASE de celui-ci. J'ai fini par utiliser ceci: + (CASE WHEN DATENAME (dw, @StartDate) = 'Saturday' THEN 1 ELSE 0 END) - (CASE WHEN DATENAME (dw, @EndDate) = 'Saturday' THEN 1 ELSE 0 END)
Sequenzia
7
La fonction de nom de données dépend des paramètres régionaux. Une solution plus robuste mais aussi plus obscure consiste à remplacer les deux dernières lignes par:-(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
Pour clarifier le commentaire de @ Sequenzia, vous supprimeriez entièrement les déclarations de cas sur dimanche, en laissant uniquement+(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
Dans Calcul des jours de travail, vous pouvez trouver un bon article sur ce sujet, mais comme vous pouvez le voir, ce n'est pas si avancé.
--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 vous devez utiliser un calendrier personnalisé, vous devrez peut-être ajouter des vérifications et des paramètres. Espérons que cela fournira un bon point de départ.
Merci d'avoir inclus le lien pour comprendre comment cela fonctionne. L'écriture sur sqlservercentral était super!
Chris Porter
20
Tout le crédit à Bogdan Maxim et Peter Mortensen. Ceci est leur message, je viens d'ajouter des vacances à la fonction (cela suppose que vous ayez une table "tblHolidays" avec un champ datetime "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)
*/
Bonjour Dan B. Juste pour vous faire savoir que votre version suppose que la table tblHolidays ne contient pas les samedis et lundis, ce qui arrive parfois. Quoi qu'il en soit, merci de partager votre version. Cheers
Julio Nobre
3
Julio - Oui - Ma version suppose que les samedis et dimanches (et non les lundis) sont des week-ends, et donc pas des jours «non ouvrés». Mais si vous travaillez le week-end, alors je suppose que tous les jours est un "jour de travail" et vous pouvez commenter la partie samedi et dimanche de la clause et ajouter simplement toutes vos vacances au tableau tblHolidays.
Dan B
1
Merci Dan. J'ai incorporé cela dans ma fonction, en ajoutant un chèque pour les week-ends car ma table DateDimensions comprend toutes les dates, jours fériés, etc. En prenant votre fonction, je viens d'ajouter: et IsWeekend = 0 après où [HolDate] entre StartDate et EndDate)
AlsoKnownAsJazz
Si la table Vacances contient des jours fériés le week-end, vous pouvez modifier les critères comme ceci: WHERE HolDate BETWEEN @StartDate AND @EndDate AND DATEPART(dw, HolDate) BETWEEN 2 AND 6pour ne compter que les jours fériés du lundi au vendredi.
Andre
7
Une autre approche pour calculer les jours ouvrables consiste à utiliser une boucle WHILE qui effectue une itération dans une plage de dates et l'incrémente de 1 chaque fois que les jours se situent entre le lundi et le vendredi. Le script complet de calcul des jours ouvrables à l'aide de la boucle WHILE est présenté ci-dessous:
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
Bien que l'option de boucle WHILE soit plus propre et utilise moins de lignes de code, elle a le potentiel d'être un goulot d'étranglement des performances dans votre environnement, en particulier lorsque votre plage de dates s'étend sur plusieurs années.
Ma version de la réponse acceptée en tant que fonction utilisant DATEPART, donc je n'ai pas à faire une comparaison de chaînes sur la ligne avec
DATENAME(dw,@StartDate)='Sunday'
Bref, voici ma fonction datée de mon entreprise
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 vous publiez des échantillons de code, XML ou de données, veuillez mettre en évidence ces lignes dans l'éditeur de texte et cliquer sur le bouton "exemples de code" ({}) dans la barre d'outils de l'éditeur pour bien le formater et la mettre en évidence!
marc_s
Génial, pas besoin de fonctions périphériques ou de mises à jour de la base de données à l'aide de cela. Merci. Love the saltire btw :-)
Brian Scott
Super solution. J'ai sous-titré des formules pour les variables à utiliser dans un univers webi pour calculer les jours de la semaine (MF) entre les dates dans 2 colonnes de tableau comme suit ... (((DATEDIFF (day, table.col1, table.col2) +1) - ((CASE DATENAME (jour de la semaine, table.col2) WHEN 'samedi' ALORS 1 QUAND 'dimanche' PUIS 2 AUTRE 0 FIN))) / 7) * 5) + (((DATEDIFF (jour, table.col1, table.col2 ) +1) - ((CASE DATENAME (jour de la semaine, table.col2) WHEN 'samedi' ALORS 1 QUAND 'dimanche' PUIS 2 AUTRE 0 FIN)))% 7)
Hilary
5
(Je suis à quelques points timide des privilèges de commentaire)
Si vous décidez de renoncer au +1 jour dans la solution élégante de CMS , notez que si vos dates de début et de fin sont dans le même week-end, vous obtenez une réponse négative. Ie., 2008/10/26 à 2008/10/26 renvoie -1.
Concernant vos soustractions de vacances. Que faire si la date de début est le 1er janvier et la date de fin le 31 décembre? Vous ne soustrayez que 2 - ce qui est faux. Je propose d'utiliser DATEDIFF (jour, Start_Date, Date) et même pour End_Date au lieu de tout 'SELECT COUNT (*) FROM Holiday ...'.
Illia Ratkevych
4
Voici une version qui fonctionne bien (je pense). La table Holiday contient des colonnes Holiday_date qui contiennent les jours fériés que votre entreprise observe.
Ces dates de vacances peuvent également tomber le week-end. Et pour certains, les vacances du dimanche seront remplacées par le lundi suivant.
Irawan Soetomo
3
Je sais que c'est une vieille question, mais j'avais besoin d'une formule pour les jours ouvrables à l'exclusion de la date de début car j'ai plusieurs éléments et j'ai besoin que les jours s'accumulent correctement.
Aucune des réponses non itératives n'a fonctionné pour moi.
J'ai utilisé une définition comme
Nombre de passages de minuit au lundi, mardi, mercredi, jeudi et vendredi
(d'autres peuvent compter de minuit à samedi au lieu de lundi)
Celui-là l'a fait pour moi mais j'ai dû faire un petit changement. Cela ne tenait pas compte de quand @StartDateest un samedi ou un vendredi. Voici ma version: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, samedi à dimanche renvoie 0, samedi à lundi renvoie 1, vendredi à samedi renvoie 0. Tout est conforme à ma définition. Votre code ne s'accumulera pas correctement (par exemple, retournez 6 pour vendredi à vendredi mais 5 pour lundi à lundi)
adrianm
3
Il s'agit essentiellement de la réponse de CMS sans dépendre d'un paramètre de langue particulier. Et comme nous visons un générique, cela signifie qu'il devrait également fonctionner pour tous les @@datefirstparamètres.
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, ...) utilise toujours une limite du samedi au dimanche pendant des semaines, de sorte que l'expression est déterministe et n'a pas besoin d'être modifiée (tant que notre définition des jours de la semaine est systématiquement du lundi au vendredi.) La numérotation des jours varie en fonction de la @@datefirst paramètre et de la les calculs modifiés gèrent cette correction avec la petite complication d'une certaine arithmétique modulaire.
Une façon plus propre de gérer la chose samedi / dimanche consiste à traduire les dates avant d'extraire une valeur de jour de semaine. Après le décalage, les valeurs seront de nouveau alignées avec une numérotation fixe (et probablement plus familière) qui commence par 1 le dimanche et se termine par 7 le samedi.
J'ai retracé cette forme de solution au moins jusqu'en 2002 et un article d'Itzik Ben-Gan. ( https://technet.microsoft.com/en-us/library/aa175781(v=sql.80).aspx ) Bien qu'il ait fallu un petit ajustement depuis le plus récentdate types ne permettent pas l'arithmétique des dates, il est par ailleurs identique.
EDIT: J'ai rajouté le +1qui avait en quelque sorte été laissé de côté. Il convient également de noter que cette méthode compte toujours les jours de début et de fin. Il suppose également que la date de fin est identique ou postérieure à la date de début.
Notez que cela renverra des résultats erronés pour de nombreuses dates du week-end afin qu'ils ne s'ajoutent pas (ven-> lun devrait être identique à ven-> sam + sam-> dim + dim-> lun). Ven-> Sam devrait être 0 (correct), Sat-> Sun devrait être 0 (faux -1), Sun-> Mon devrait être 1 (faux 0). Les autres erreurs qui en découlent sont Sam-> Sat = -1, Sun-> Sun = -1, Sun-> Sat = 4
adrianm
@adrianm Je crois que j'ai corrigé les problèmes. En fait, le problème était que c'était toujours un coup parce que j'avais en quelque sorte laissé tomber cette pièce par accident.
shawnt00
Merci pour la mise à jour. Je pensais que votre formule excluait la date de début, ce dont j'avais besoin. Je l'ai résolu moi-même et l'a ajouté comme une autre réponse.
adrianm
2
À l'aide d'une table de dates:
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 vous ne l'avez pas, vous pouvez utiliser une table de nombres:
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
Ils devraient tous les deux être rapides et cela élimine l'ambiguïté / complexité. La première option est la meilleure, mais si vous n'avez pas de table de calendrier, vous pouvez toujours créer une table de nombres avec 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
Si vous envisagez d'utiliser une fonction, il serait peut-être préférable d'utiliser une fonction basée sur une table comme dans la réponse de Mário Meyrelles
James Jenkins
1
CREATEFUNCTION x
(@StartDate DATETIME,@EndDate DATETIME
)
RETURNS INT
ASBEGINDECLARE@Teller INT
SET@StartDate = DATEADD(dd,1,@StartDate)SET@Teller =0IF DATEDIFF(dd,@StartDate,@EndDate)<=0BEGINSET@Teller =0ENDELSEBEGINWHILE
DATEDIFF(dd,@StartDate,@EndDate)>=0BEGINIF DATEPART(dw,@StartDate)<6BEGINSET@Teller =@Teller +1ENDSET@StartDate = DATEADD(dd,1,@StartDate)ENDENDRETURN@Teller
END
J'ai pris les différents exemples ici, mais dans ma situation particulière, nous avons un @PromisedDate pour la livraison et un @ReceivedDate pour la réception effective de l'article. Lorsqu'un article était reçu avant la "PromisedDate", les calculs ne totalisaient pas correctement à moins que j'aie commandé les dates passées dans la fonction par ordre calendaire. Ne voulant pas vérifier les dates à chaque fois, j'ai changé la fonction pour gérer cela pour moi.
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 vous devez ajouter des jours ouvrés à une date donnée, vous pouvez créer une fonction qui dépend d'un tableau de calendrier, décrit ci-dessous:
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
Comme pour DATEDIFF, je ne considère pas que la date de fin fait partie de l'intervalle. Le nombre de dimanches (par exemple) entre @StartDate et @EndDate est le nombre de dimanches entre un lundi "initial" et le @EndDate moins le nombre de dimanches entre ce lundi "initial" et le @StartDate. Sachant cela, nous pouvons calculer le nombre de jours de travail comme suit:
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
J'ai trouvé le TSQL ci-dessous une solution assez élégante (je n'ai pas les autorisations pour exécuter des fonctions). J'ai trouvé les DATEDIFFignoresDATEFIRST et je voulais que mon premier jour de la semaine soit un lundi. Je voulais aussi que le premier jour ouvrable soit mis à zéro et s'il tombe un week-end, le lundi sera à zéro. Cela peut aider quelqu'un qui a une exigence légèrement différente :)
Il ne gère pas les jours fériés
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*/
Une approche consiste à «parcourir les dates» du début à la fin en conjonction avec une expression de cas qui vérifie si le jour n'est pas un samedi ou un dimanche et le signale (1 pour la semaine, 0 pour le week-end). Et à la fin, il suffit de additionner les indicateurs (ce serait égal au nombre de 1-flags car l'autre drapeau est 0) pour vous donner le nombre de jours de la semaine.
Vous pouvez utiliser un type de fonction utilitaire GetNums (startNumber, endNumber) qui génère une série de nombres pour la «boucle» de la date de début à la date de fin. Référez-vous à http://tsql.solidq.com/SourceCodes/GetNums.txt pour une implémentation. La logique peut également être étendue pour répondre aux vacances (par exemple si vous avez une table de vacances)
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)
J'ai emprunté des idées à d'autres pour créer ma solution. J'utilise le code en ligne pour ignorer les week-ends et les jours fériés aux États-Unis. Dans mon environnement, EndDate peut être nul, mais il ne précédera jamais 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
Réponses:
Pour les jours ouvrables, du lundi au vendredi, vous pouvez le faire avec un seul SELECT, comme ceci:
Si vous voulez inclure des vacances, vous devez vous en sortir un peu ...
la source
-(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)
Dans Calcul des jours de travail, vous pouvez trouver un bon article sur ce sujet, mais comme vous pouvez le voir, ce n'est pas si avancé.
Si vous devez utiliser un calendrier personnalisé, vous devrez peut-être ajouter des vérifications et des paramètres. Espérons que cela fournira un bon point de départ.
la source
Tout le crédit à Bogdan Maxim et Peter Mortensen. Ceci est leur message, je viens d'ajouter des vacances à la fonction (cela suppose que vous ayez une table "tblHolidays" avec un champ datetime "HolDate".
la source
WHERE HolDate BETWEEN @StartDate AND @EndDate AND DATEPART(dw, HolDate) BETWEEN 2 AND 6
pour ne compter que les jours fériés du lundi au vendredi.Une autre approche pour calculer les jours ouvrables consiste à utiliser une boucle WHILE qui effectue une itération dans une plage de dates et l'incrémente de 1 chaque fois que les jours se situent entre le lundi et le vendredi. Le script complet de calcul des jours ouvrables à l'aide de la boucle WHILE est présenté ci-dessous:
Bien que l'option de boucle WHILE soit plus propre et utilise moins de lignes de code, elle a le potentiel d'être un goulot d'étranglement des performances dans votre environnement, en particulier lorsque votre plage de dates s'étend sur plusieurs années.
Vous pouvez voir plus de méthodes sur la façon de calculer les jours et les heures de travail dans cet article: https://www.sqlshack.com/how-to-calculate-work-days-and-hours-in-sql-server/
la source
Ma version de la réponse acceptée en tant que fonction utilisant
DATEPART
, donc je n'ai pas à faire une comparaison de chaînes sur la ligne avecBref, voici ma fonction datée de mon entreprise
la source
la source
(Je suis à quelques points timide des privilèges de commentaire)
Si vous décidez de renoncer au +1 jour dans la solution élégante de CMS , notez que si vos dates de début et de fin sont dans le même week-end, vous obtenez une réponse négative. Ie., 2008/10/26 à 2008/10/26 renvoie -1.
ma solution plutôt simpliste:
.. qui définit également tous les messages erronés avec une date de début après la date de fin à zéro. Quelque chose que vous recherchez ou non.
la source
Pour la différence entre les dates, y compris les vacances, je suis allé de cette façon:
1) Table avec jours fériés:
2) J'avais ma table de plannings comme celle-ci et je voulais remplir la colonne Work_Days qui était vide:
3) Donc, pour que "Work_Days" remplisse plus tard ma colonne, il suffisait de:
J'espère que je pourrais aider.
À votre santé
la source
Voici une version qui fonctionne bien (je pense). La table Holiday contient des colonnes Holiday_date qui contiennent les jours fériés que votre entreprise observe.
la source
Je sais que c'est une vieille question, mais j'avais besoin d'une formule pour les jours ouvrables à l'exclusion de la date de début car j'ai plusieurs éléments et j'ai besoin que les jours s'accumulent correctement.
Aucune des réponses non itératives n'a fonctionné pour moi.
J'ai utilisé une définition comme
(d'autres peuvent compter de minuit à samedi au lieu de lundi)
J'ai fini avec cette formule
la source
@StartDate
est un samedi ou un vendredi. Voici ma version: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
Il s'agit essentiellement de la réponse de CMS sans dépendre d'un paramètre de langue particulier. Et comme nous visons un générique, cela signifie qu'il devrait également fonctionner pour tous les
@@datefirst
paramètres.datediff(week, ...)
utilise toujours une limite du samedi au dimanche pendant des semaines, de sorte que l'expression est déterministe et n'a pas besoin d'être modifiée (tant que notre définition des jours de la semaine est systématiquement du lundi au vendredi.) La numérotation des jours varie en fonction de la@@datefirst
paramètre et de la les calculs modifiés gèrent cette correction avec la petite complication d'une certaine arithmétique modulaire.Une façon plus propre de gérer la chose samedi / dimanche consiste à traduire les dates avant d'extraire une valeur de jour de semaine. Après le décalage, les valeurs seront de nouveau alignées avec une numérotation fixe (et probablement plus familière) qui commence par 1 le dimanche et se termine par 7 le samedi.
J'ai retracé cette forme de solution au moins jusqu'en 2002 et un article d'Itzik Ben-Gan. ( https://technet.microsoft.com/en-us/library/aa175781(v=sql.80).aspx ) Bien qu'il ait fallu un petit ajustement depuis le plus récent
date
types ne permettent pas l'arithmétique des dates, il est par ailleurs identique.EDIT: J'ai rajouté le
+1
qui avait en quelque sorte été laissé de côté. Il convient également de noter que cette méthode compte toujours les jours de début et de fin. Il suppose également que la date de fin est identique ou postérieure à la date de début.la source
À l'aide d'une table de dates:
Si vous ne l'avez pas, vous pouvez utiliser une table de nombres:
Ils devraient tous les deux être rapides et cela élimine l'ambiguïté / complexité. La première option est la meilleure, mais si vous n'avez pas de table de calendrier, vous pouvez toujours créer une table de nombres avec un CTE.
la source
la source
la source
J'ai pris les différents exemples ici, mais dans ma situation particulière, nous avons un @PromisedDate pour la livraison et un @ReceivedDate pour la réception effective de l'article. Lorsqu'un article était reçu avant la "PromisedDate", les calculs ne totalisaient pas correctement à moins que j'aie commandé les dates passées dans la fonction par ordre calendaire. Ne voulant pas vérifier les dates à chaque fois, j'ai changé la fonction pour gérer cela pour moi.
la source
Si vous devez ajouter des jours ouvrés à une date donnée, vous pouvez créer une fonction qui dépend d'un tableau de calendrier, décrit ci-dessous:
la source
Comme pour DATEDIFF, je ne considère pas que la date de fin fait partie de l'intervalle. Le nombre de dimanches (par exemple) entre @StartDate et @EndDate est le nombre de dimanches entre un lundi "initial" et le @EndDate moins le nombre de dimanches entre ce lundi "initial" et le @StartDate. Sachant cela, nous pouvons calculer le nombre de jours de travail comme suit:
Meilleures salutations!
la source
Cela fonctionne pour moi, dans mon pays le samedi et le dimanche sont des jours non ouvrables.
Pour moi, l'heure de @StartDate et de @EndDate est importante.
la source
Créer une fonction comme:
Vous pouvez appeler la fonction comme:
Ou comme:
la source
Fin
la source
J'ai trouvé le TSQL ci-dessous une solution assez élégante (je n'ai pas les autorisations pour exécuter des fonctions). J'ai trouvé les
DATEDIFF
ignoresDATEFIRST
et je voulais que mon premier jour de la semaine soit un lundi. Je voulais aussi que le premier jour ouvrable soit mis à zéro et s'il tombe un week-end, le lundi sera à zéro. Cela peut aider quelqu'un qui a une exigence légèrement différente :)Il ne gère pas les jours fériés
la source
Une approche consiste à «parcourir les dates» du début à la fin en conjonction avec une expression de cas qui vérifie si le jour n'est pas un samedi ou un dimanche et le signale (1 pour la semaine, 0 pour le week-end). Et à la fin, il suffit de additionner les indicateurs (ce serait égal au nombre de 1-flags car l'autre drapeau est 0) pour vous donner le nombre de jours de la semaine.
Vous pouvez utiliser un type de fonction utilitaire GetNums (startNumber, endNumber) qui génère une série de nombres pour la «boucle» de la date de début à la date de fin. Référez-vous à http://tsql.solidq.com/SourceCodes/GetNums.txt pour une implémentation. La logique peut également être étendue pour répondre aux vacances (par exemple si vous avez une table de vacances)
la source
J'ai emprunté des idées à d'autres pour créer ma solution. J'utilise le code en ligne pour ignorer les week-ends et les jours fériés aux États-Unis. Dans mon environnement, EndDate peut être nul, mais il ne précédera jamais StartDate.
la source