Я только что понял, что этот код не всегда работает! я попробовал это: SET @StartDate = '28 -mar-2011 'SET @EndDate = '29 -mar-2011' ответ, который он считал за 2 дня
greektreat
16
@ greektreat Работает нормально. Просто @StartDate и @EndDate включены в счетчик. Если вы хотите, чтобы с понедельника по вторник считались 1 днем, просто удалите «+ 1» после первого DATEDIFF. Тогда вы также получите пт-> сб = 0, пт-> вс = 0, пт-> пн = 1.
Джо Дейли
6
Как продолжение к @JoeDaley. Когда вы удаляете + 1 после DATEDIFF, чтобы исключить начальную дату из подсчета, вам также необходимо настроить часть CASE этого. Я закончил тем, что использовал это: + (CASE WHEN DATENAME (dw, @StartDate) = 'Суббота' THEN 1 ELSE 0 END) - (CASE WHEN DATENAME (dw, @EndDate) = 'Суббота' THEN 1 ELSE 0 END)
Sequenzia
7
Функция datename зависит от локали. Более надежное, но и более неясное решение состоит в том, чтобы заменить последние две строки на:-(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)
Торбен Кляйн
2
Чтобы прояснить комментарий @ Sequenzia, вы бы УДАЛИТЬ заявления по делу о воскресенье полностью, оставив только+(CASE WHEN DATENAME(dw, @StartDate) = 'Saturday' THEN 1 ELSE 0 END) - (CASE WHEN DATENAME(dw, @EndDate) = 'Saturday' THEN 1 ELSE 0 END)
Энди Раддац
32
В « Расчет рабочих дней» вы можете найти хорошую статью на эту тему, но, как вы видите, она не настолько продвинута.
--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
Если вам нужно использовать собственный календарь, вам может потребоваться добавить некоторые проверки и некоторые параметры. Надеюсь, это обеспечит хорошую отправную точку.
Спасибо за ссылку, чтобы понять, как это работает. Запись на sqlservercentral была отличной!
Крис Портер
20
Все заслуги Богдана Максима и Питера Мортенсена. Это их пост, я только что добавил праздники в функцию (Предполагается, что у вас есть таблица "tblHolidays" с полем даты и времени "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)
*/
Привет, Дэн Б. Просто хочу сообщить, что ваша версия предполагает, что таблицы tblHolidays не содержат суббот и понедельников, что иногда случается. В любом случае, спасибо, что поделились своей версией. Приветствия
Хулио Нобре
3
Хулио - Да. Моя версия предполагает, что суббота и воскресенье (а не понедельник) - выходные, и, следовательно, не "нерабочий" день. Но если вы работаете на выходных, то я думаю, что каждый день - это «рабочий день», и вы можете закомментировать часть статьи в субботу и воскресенье и просто добавить все свои праздники в таблицу tblHolidays.
Дан Б
1
Спасибо Дэн. Я включил это в свою функцию, добавив проверку на выходные дни, так как моя таблица DateDimensions включает все даты, праздники и т. Д. Принимая вашу функцию, я просто добавил: и IsWeekend = 0 после где [HolDate] между StartDate и EndDate)
KnownAsJazz
Если в таблице «Праздники» указаны выходные дни по выходным, вы можете изменить критерии следующим образом: WHERE HolDate BETWEEN @StartDate AND @EndDate AND DATEPART(dw, HolDate) BETWEEN 2 AND 6считать выходные дни только с понедельника по пятницу.
Андре
7
Другой подход к вычислению рабочих дней состоит в том, чтобы использовать цикл WHILE, который, в основном, выполняет итерацию по диапазону дат и увеличивает его на 1 всякий раз, когда дни оказываются в пределах понедельника - пятницы. Полный скрипт для расчета рабочих дней с использованием цикла WHILE показан ниже:
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
Хотя опция цикла WHILE более чистая и использует меньше строк кода, она потенциально может стать узким местом для производительности в вашей среде, особенно когда диапазон дат охватывает несколько лет.
Моя версия принятого ответа в качестве функции использует DATEPART, поэтому мне не нужно делать сравнение строк в строке с
DATENAME(dw,@StartDate)='Sunday'
Во всяком случае, вот моя функция Datediff бизнеса
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
Если почтовый индекс, XML или данных образцов, пожалуйста , выделите эти строки в текстовом редакторе и нажмите на кнопку «код образцы» ({}) на панели инструментов редактора , чтобы красиво формат и синтаксис выделить его!
marc_s
Отлично, нет необходимости в периферийных функциях или обновлениях базы данных, использующих это. Спасибо. Любовь сальтиры между прочим :-)
Брайан Скотт
Супер решение. Я добавил в формулы формулы для переменных, которые будут использоваться в Webi Universe для вычисления дней недели (MF) между датами в 2 столбцах таблицы, например, так ... ((((DATEDIFF (день, table.col1, table.col2) +1) - ((CASE DATENAME (weekday, table.col2) КОГДА «Суббота», ТО 1, КОГДА «Воскресенье», ТО 2, ЛИБО 0 КОНЕЦ))) / 7) * 5) + (((DATEDIFF (день, table.col1, table.col2 ) +1) - ((CASE DATENAME (день недели, table.col2) КОГДА «Суббота», ТО 1, КОГДА «Воскресенье», ТО 2, ЛИБО 0 КОНЕЦ)))% 7)
Хилари
5
(Я на несколько пунктов стесняюсь комментировать привилегии)
Если вы решите отказаться от +1 дня в элегантном решении CMS , учтите, что если ваши начальная и конечная даты совпадают, вы получите отрицательный ответ. Т.е. с 2008/10/26 по 2008/10/26 возвращается -1.
По поводу вашего отпуска вычитания. Что если дата начала - 1 января, а дата окончания - 31 декабря? Вы вычтете только 2 - что неправильно. Я предлагаю использовать DATEDIFF (день, Start_Date, Date) и то же самое для End_Date вместо всего «SELECT COUNT (*) FROM Holiday ...».
Илья Раткевич
4
Вот версия, которая хорошо работает (я думаю). Таблица выходных содержит столбцы Holiday_date, которые содержат данные о праздничных днях, которые наблюдает ваша компания.
Эти праздничные даты могут выпадать и на выходные. А для некоторых праздник в воскресенье сменится на следующий понедельник.
Ираван Соетомо
3
Я знаю, что это старый вопрос, но мне нужна формула для рабочих дней, исключая дату начала, так как у меня есть несколько пунктов, и мне нужно, чтобы дни накапливались правильно.
Ни один из не повторяющихся ответов не работал для меня.
Я использовал определение как
Количество переходов с полуночи на понедельник, вторник, среду, четверг и пятницу
(другие могут считать от полуночи до субботы вместо понедельника)
Тот сделал это для меня, но я должен был сделать небольшое изменение. Не было учета того, когда @StartDateсуббота или пятница. Вот моя версия: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, с субботы по воскресенье возвращает 0, с субботы по понедельник возвращает 1, с пятницы на субботу возвращает 0. Все соответствуют моему определению. Ваш код не будет накапливаться правильно (например, верните 6 с пятницы по пятницу, но 5 с понедельника по понедельник)
adrianm
3
Это в основном ответ CMS без опоры на конкретную языковую настройку. А поскольку мы снимаем для общего, это означает, что он должен работать и для всех @@datefirstнастроек.
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, ...)всегда используется граница от субботы до воскресенья в течение нескольких недель, так что выражение является детерминированным и не нуждается в изменении (при условии, что наше определение дней недели последовательно с понедельника по пятницу.) Нумерация дней зависит от @@datefirstнастроек и модифицированные вычисления обрабатывают эту поправку с небольшим усложнением некоторой модульной арифметики.
Более понятный способ справиться с субботой / воскресеньем - это перевести даты до получения значения дня недели. После сдвига значения вернутся в соответствие с фиксированной (и, вероятно, более знакомой) нумерацией, которая начинается с 1 в воскресенье и заканчивается 7 в субботу.
Я проследил эту форму решения еще в 2002 году и опубликовал статью Ицик Бен-Гана. ( https://technet.microsoft.com/en-us/library/aa175781(v=sql.80).aspx ) Хотя для этого потребовалась небольшая настройка, поскольку более новые dateтипы не допускают арифметику дат, в остальном она идентична.
РЕДАКТИРОВАТЬ: я добавил обратно, +1что-то было остановлено. Стоит также отметить, что этот метод всегда считает дни начала и окончания. Предполагается также, что дата окончания находится на или после даты начала.
Обратите внимание, что это даст неправильные результаты для многих дат в выходные дни, поэтому они не добавляют upp (пт-> пн должно совпадать с пт-> сб + сб-> вс + вс-> понедельник). Fri-> Sat должно быть 0 (правильно), Sat-> Sun должно быть 0 (неправильно -1), Sun-> Mon должно быть 1 (неправильно 0). Другие ошибки, вытекающие из этого, являются Sat-> Sat = -1, Sun-> Sun = -1, Sun-> Sat = 4
adrianm
@adrianm Я думаю, что я исправил проблемы. На самом деле проблема была в том, что он всегда отключался, потому что я как-то случайно уронил эту часть.
shawnt00
Спасибо за обновление. Я думал, что ваша формула исключает дату начала, что мне и нужно. Решил сам и добавил в качестве другого ответа.
Адриан
2
Использование таблицы дат:
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;
Если у вас его нет, вы можете использовать таблицу чисел:
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
Они оба должны быть быстрыми, и это устраняет двусмысленность / сложность. Первый вариант самый лучший, но если у вас нет таблицы календаря, вы всегда можете создать таблицу чисел с 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
Я взял различные примеры здесь, но в моей конкретной ситуации у нас есть @PromisedDate для доставки и @ReceivedDate для фактического получения товара. Когда элемент был получен до «PromisedDate», вычисления не суммировались правильно, если я не упорядочил даты, переданные в функцию, по календарному порядку. Не желая каждый раз проверять даты, я изменил функцию, чтобы справиться с этим для меня.
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
Если вам нужно добавить рабочие дни к определенной дате, вы можете создать функцию, которая зависит от календарной таблицы, описанной ниже:
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
Как и в случае с DATEDIFF, я не считаю, что дата окончания является частью интервала. Количество (например) воскресений между @StartDate и @EndDate - это количество воскресений между «начальным» понедельником и @EndDate минус количество воскресений между этим «начальным» понедельником и @StartDate. Зная это, мы можем рассчитать количество рабочих дней следующим образом:
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
Я нашел ниже TSQL довольно элегантное решение (у меня нет прав для запуска функций). Я нашел DATEDIFFигнорирование DATEFIRSTи хотел, чтобы моим первым днем недели был понедельник. Я также хотел, чтобы первый рабочий день был установлен на ноль, и если он выпадает на выходные, понедельник будет нулевым. В этом может помочь тот, у кого немного другое требование :)
Не работает в праздничные дни
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*/
Один из подходов заключается в «обходе дат» от начала до конца в сочетании с выражением регистра, которое проверяет, не является ли день субботой или воскресеньем, и помечает его (1 для дня недели, 0 для выходных). И в конце просто сложите флаги (это будет равно количеству 1-флагов, так как другой флаг равен 0), чтобы получить количество дней недели.
Вы можете использовать вспомогательную функцию типа GetNums (startNumber, endNumber), которая генерирует серию чисел для «циклического преобразования» от начальной даты до конечной даты. См. Http://tsql.solidq.com/SourceCodes/GetNums.txt для реализации. Логика также может быть расширена для обслуживания праздничных дней (скажем, если у вас есть праздничный стол)
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)
Я позаимствовал некоторые идеи у других, чтобы создать свое решение. Я использую встроенный код, чтобы игнорировать выходные и федеральные праздники США. В моей среде EndDate может быть нулевым, но он никогда не будет предшествовать 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
Ответы:
Для рабочих дней, с понедельника по пятницу, вы можете сделать это с помощью одного SELECT, например:
Если вы хотите включить праздники, вы должны немного поработать ...
источник
-(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)
В « Расчет рабочих дней» вы можете найти хорошую статью на эту тему, но, как вы видите, она не настолько продвинута.
Если вам нужно использовать собственный календарь, вам может потребоваться добавить некоторые проверки и некоторые параметры. Надеюсь, это обеспечит хорошую отправную точку.
источник
Все заслуги Богдана Максима и Питера Мортенсена. Это их пост, я только что добавил праздники в функцию (Предполагается, что у вас есть таблица "tblHolidays" с полем даты и времени "HolDate".
источник
WHERE HolDate BETWEEN @StartDate AND @EndDate AND DATEPART(dw, HolDate) BETWEEN 2 AND 6
считать выходные дни только с понедельника по пятницу.Другой подход к вычислению рабочих дней состоит в том, чтобы использовать цикл WHILE, который, в основном, выполняет итерацию по диапазону дат и увеличивает его на 1 всякий раз, когда дни оказываются в пределах понедельника - пятницы. Полный скрипт для расчета рабочих дней с использованием цикла WHILE показан ниже:
Хотя опция цикла WHILE более чистая и использует меньше строк кода, она потенциально может стать узким местом для производительности в вашей среде, особенно когда диапазон дат охватывает несколько лет.
Вы можете ознакомиться с другими методами расчета рабочих дней и часов в этой статье: https://www.sqlshack.com/how-to-calculate-work-days-and-hours-in-sql-server/
источник
Моя версия принятого ответа в качестве функции использует
DATEPART
, поэтому мне не нужно делать сравнение строк в строке сВо всяком случае, вот моя функция Datediff бизнеса
источник
источник
(Я на несколько пунктов стесняюсь комментировать привилегии)
Если вы решите отказаться от +1 дня в элегантном решении CMS , учтите, что если ваши начальная и конечная даты совпадают, вы получите отрицательный ответ. Т.е. с 2008/10/26 по 2008/10/26 возвращается -1.
мое довольно упрощенное решение:
.. который также устанавливает все ошибочные сообщения с начальной датой после конечной даты равной нулю. То, что вы можете или не можете искать.
источник
Для разницы между датами, включая праздники, я пошел следующим образом:
1) Таблица с праздниками:
2) У меня была такая таблица планирования, и я хотел заполнить столбец Work_Days, который был пустым:
3) Таким образом, чтобы получить «Work_Days» для последующего заполнения моего столбца, нужно было:
Надеюсь, что смогу помочь.
ура
источник
Вот версия, которая хорошо работает (я думаю). Таблица выходных содержит столбцы Holiday_date, которые содержат данные о праздничных днях, которые наблюдает ваша компания.
источник
Я знаю, что это старый вопрос, но мне нужна формула для рабочих дней, исключая дату начала, так как у меня есть несколько пунктов, и мне нужно, чтобы дни накапливались правильно.
Ни один из не повторяющихся ответов не работал для меня.
Я использовал определение как
(другие могут считать от полуночи до субботы вместо понедельника)
Я закончил с этой формулой
источник
@StartDate
суббота или пятница. Вот моя версия: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
Это в основном ответ CMS без опоры на конкретную языковую настройку. А поскольку мы снимаем для общего, это означает, что он должен работать и для всех
@@datefirst
настроек.datediff(week, ...)
всегда используется граница от субботы до воскресенья в течение нескольких недель, так что выражение является детерминированным и не нуждается в изменении (при условии, что наше определение дней недели последовательно с понедельника по пятницу.) Нумерация дней зависит от@@datefirst
настроек и модифицированные вычисления обрабатывают эту поправку с небольшим усложнением некоторой модульной арифметики.Более понятный способ справиться с субботой / воскресеньем - это перевести даты до получения значения дня недели. После сдвига значения вернутся в соответствие с фиксированной (и, вероятно, более знакомой) нумерацией, которая начинается с 1 в воскресенье и заканчивается 7 в субботу.
Я проследил эту форму решения еще в 2002 году и опубликовал статью Ицик Бен-Гана. ( https://technet.microsoft.com/en-us/library/aa175781(v=sql.80).aspx ) Хотя для этого потребовалась небольшая настройка, поскольку более новые
date
типы не допускают арифметику дат, в остальном она идентична.РЕДАКТИРОВАТЬ: я добавил обратно,
+1
что-то было остановлено. Стоит также отметить, что этот метод всегда считает дни начала и окончания. Предполагается также, что дата окончания находится на или после даты начала.источник
Использование таблицы дат:
Если у вас его нет, вы можете использовать таблицу чисел:
Они оба должны быть быстрыми, и это устраняет двусмысленность / сложность. Первый вариант самый лучший, но если у вас нет таблицы календаря, вы всегда можете создать таблицу чисел с CTE.
источник
источник
источник
Я взял различные примеры здесь, но в моей конкретной ситуации у нас есть @PromisedDate для доставки и @ReceivedDate для фактического получения товара. Когда элемент был получен до «PromisedDate», вычисления не суммировались правильно, если я не упорядочил даты, переданные в функцию, по календарному порядку. Не желая каждый раз проверять даты, я изменил функцию, чтобы справиться с этим для меня.
источник
Если вам нужно добавить рабочие дни к определенной дате, вы можете создать функцию, которая зависит от календарной таблицы, описанной ниже:
источник
Как и в случае с DATEDIFF, я не считаю, что дата окончания является частью интервала. Количество (например) воскресений между @StartDate и @EndDate - это количество воскресений между «начальным» понедельником и @EndDate минус количество воскресений между этим «начальным» понедельником и @StartDate. Зная это, мы можем рассчитать количество рабочих дней следующим образом:
С уважением!
источник
Это работает для меня, в моей стране суббота и воскресенье нерабочие дни.
Для меня важно время @StartDate и @EndDate.
источник
Создать функцию как:
Вы можете вызвать функцию как:
Или как:
источник
Конец
источник
Я нашел ниже TSQL довольно элегантное решение (у меня нет прав для запуска функций). Я нашел
DATEDIFF
игнорированиеDATEFIRST
и хотел, чтобы моим первым днем недели был понедельник. Я также хотел, чтобы первый рабочий день был установлен на ноль, и если он выпадает на выходные, понедельник будет нулевым. В этом может помочь тот, у кого немного другое требование :)Не работает в праздничные дни
источник
Один из подходов заключается в «обходе дат» от начала до конца в сочетании с выражением регистра, которое проверяет, не является ли день субботой или воскресеньем, и помечает его (1 для дня недели, 0 для выходных). И в конце просто сложите флаги (это будет равно количеству 1-флагов, так как другой флаг равен 0), чтобы получить количество дней недели.
Вы можете использовать вспомогательную функцию типа GetNums (startNumber, endNumber), которая генерирует серию чисел для «циклического преобразования» от начальной даты до конечной даты. См. Http://tsql.solidq.com/SourceCodes/GetNums.txt для реализации. Логика также может быть расширена для обслуживания праздничных дней (скажем, если у вас есть праздничный стол)
источник
Я позаимствовал некоторые идеи у других, чтобы создать свое решение. Я использую встроенный код, чтобы игнорировать выходные и федеральные праздники США. В моей среде EndDate может быть нулевым, но он никогда не будет предшествовать StartDate.
источник