如何计算SQL Server 2008中表格(从第1行到结尾)的两个日期之间的工作天数?
我试过这样的东西,但它不起作用
DECLARE @StartDate as DATETIME, @EndDate as DATETIME
Select @StartDate = date2 from testtable ;
select @EndDate = date1 from testtable ;
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)
答案 0 :(得分:6)
我总是推荐一个Calendar table,然后你可以简单地使用:
SELECT COUNT(*)
FROM dbo.CalendarTable
WHERE IsWorkingDay = 1
AND [Date] > @StartDate
AND [Date] <= @EndDate;
由于SQL不了解国定假日,例如两个日期之间的工作日数并不总是代表工作日数。这就是大多数数据库必须使用日历表的原因。它们不占用大量内存并简化了大量查询。
但如果这不是一个选项,那么您可以在运行中相对轻松地生成日期表并使用此
SET DATEFIRST 1;
DECLARE @StartDate DATETIME = '20131103',
@EndDate DATETIME = '20131104';
-- GENERATE A LIST OF ALL DATES BETWEEN THE START DATE AND THE END DATE
WITH AllDates AS
( SELECT TOP (DATEDIFF(DAY, @StartDate, @EndDate))
D = DATEADD(DAY, ROW_NUMBER() OVER(ORDER BY a.Object_ID), @StartDate)
FROM sys.all_objects a
CROSS JOIN sys.all_objects b
)
SELECT WeekDays = COUNT(*)
FROM AllDates
WHERE DATEPART(WEEKDAY, D) NOT IN (6, 7);
修改强>
如果您需要计算两个日期列之间的差异,您仍然可以使用日历表:
SELECT t.ID,
t.Date1,
t.Date2,
WorkingDays = COUNT(c.DateKey)
FROM TestTable t
LEFT JOIN dbo.Calendar c
ON c.DateKey >= t.Date1
AND c.DateKey < t.Date2
AND c.IsWorkingDay = 1
GROUP BY t.ID, t.Date1, t.Date2;
<强> Example on SQL-Fiddle 强>
答案 1 :(得分:2)
除了日期外,它除了日期部分而不是描述。您可以将用作示例的参数替换为查询中的值。
Declare
@startdate datetime = '2013-11-01',
@enddate datetime = '2013-11-11'
SELECT
(DATEDIFF(dd, @StartDate, @EndDate) + 1)
-(DATEDIFF(wk, @StartDate, @EndDate) * 2)
-(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)
Returns 7
答案 2 :(得分:0)
要添加到GarethD的答案 - 我将日历表的美国版本的SQL放在一起,所有假期和周末设置为is_working_day = false ...对于任何想要SQL的人,在这里是:
declare @start_dt as date = '1/1/2009'; -- Date from which the calendar table will be created.
declare @end_dt as date = '1/1/2030'; -- Calendar table will be created up to this date (not including).
create table CalendarTable (
date_id date primary key,
date_year smallint,
date_month tinyint,
date_day tinyint,
weekday_id tinyint,
weekday_nm varchar(10),
month_nm varchar(10),
day_of_year smallint,
quarter_id tinyint,
first_day_of_month date,
last_day_of_month date,
start_dts datetime,
end_dts datetime,
week_number_of_month int,
is_working_day bit,
)
while @start_dt < @end_dt
begin
insert into CalendarTable(
date_id, date_year, date_month, date_day,
weekday_id, weekday_nm, month_nm, day_of_year, quarter_id,
first_day_of_month, last_day_of_month,
start_dts, end_dts, week_number_of_month, is_working_day
)
values(
@start_dt, year(@start_dt), month(@start_dt), day(@start_dt),
datepart(weekday, @start_dt), datename(weekday, @start_dt), datename(month, @start_dt), datepart(dayofyear, @start_dt), datepart(quarter, @start_dt),
dateadd(day,-(day(@start_dt)-1),@start_dt), dateadd(day,-(day(dateadd(month,1,@start_dt))),dateadd(month,1,@start_dt)),
cast(@start_dt as datetime), dateadd(second,-1,cast(dateadd(day, 1, @start_dt) as datetime)), DATEDIFF(week, DATEADD(MONTH, DATEDIFF(MONTH, 0, @start_dt), 0), @start_dt) +1, 0
)
set @start_dt = dateadd(day, 1, @start_dt)
end
GO
-- Set all non-weekend days as business days
update CalendarTable set is_working_day = 1 where weekday_id not in (1,7)
GO
-- New Years Day
update CalendarTable set is_working_day = 0 where date_month = 1 and date_day = 1
GO
-- Memorial Day (last Monday of May)
WITH Memorial_Day AS
(
SELECT date_id, date_year, date_day,
ROW_NUMBER() OVER (PARTITION BY date_year ORDER BY date_day desc) AS RowNumber
FROM CalendarTable
where date_month = 5 and weekday_id = 2
)
update CalendarTable set is_working_day = 0 where date_id in (SELECT date_id FROM Memorial_Day
where rownumber = 1)
GO
-- Independence Day
update CalendarTable set is_working_day = 0 where date_month = 7 and date_day = 4
GO
-- Labor Day (first Monday in September)
WITH Labor_Day AS
(
SELECT date_id, date_year, date_day,
ROW_NUMBER() OVER (PARTITION BY date_year ORDER BY date_day) AS RowNumber
FROM CalendarTable
where date_month = 9 and weekday_id = 2
)
update CalendarTable set is_working_day = 0 where date_id in (SELECT date_id FROM Labor_Day
where rownumber = 1)
GO
-- Thanksgiving (fourth Thursday in November)
WITH Thanksgiving AS
(
SELECT date_id, date_year, date_day,
ROW_NUMBER() OVER (PARTITION BY date_year ORDER BY date_day) AS RowNumber
FROM CalendarTable
where date_month = 11 and weekday_id = 5
)
update CalendarTable set is_working_day = 0 where date_id in (SELECT date_id FROM Thanksgiving
where rownumber = 4)
GO
-- Day After Thanksgiving (fourth Friday in November)
WITH DayAfterThanksgiving AS
(
SELECT date_id, date_year, date_day,
ROW_NUMBER() OVER (PARTITION BY date_year ORDER BY date_day) AS RowNumber
FROM CalendarTable
where date_month = 11 and weekday_id = 6
)
update CalendarTable set is_working_day = 0 where date_id in (SELECT date_id FROM DayAfterThanksgiving
where rownumber = 4)
GO
-- Christmas Day
update CalendarTable set is_working_day = 0 where date_month = 12 and date_day = 25
GO
答案 3 :(得分:0)
您可以简单地使用sql的datediff函数。然后您可以减去这些日期之间的周末(如果有)。例如,检查以下查询。 您还可以计算开始/结束日期之间的假期,并可以从最终选择中减去该假期。
Declare @startdate as DateTime
Declare @enddate as DateTime
Set @startdate = GETDATE()-2;
set @enddate = GETDATE()+3;
select @startdate,@enddate,(datediff(day,@startdate,@enddate+1)-(2)*datediff(week,@startdate,@enddate))
答案 4 :(得分:0)
有趣的问题。了解用例始终很重要。如果从星期天到星期一计算,我们想说有一天就像星期一休市之前一样。或者我们要说没有一天,好像星期一开始之前没有几天。在我们的情况下,我们需要计算两天(开始和结束)(如果它们是工作日的话),因为我正在工资核算应用程序中估算一些应计费用。周末的任何一天,以后都会根据假期和加班记录进行计算。
当我拿出日历时,我意识到从星期六或星期日开始计数时,我们可以从星期一开始计数。而且,当计数到星期六或星期日时,我可以在星期五之前停止计数。因此,我编写了一个用于调整开始日期和结束日期的函数,将周数除以7,然后乘以每周的5个工作日,然后将其相加。当我们开始在一个周末开始计数但从未到星期一时,我确实不得不考虑这种情况。
-============================================= -作者:Todd P Payne -创建日期:9/1/2018 -说明:计算两个日期之间的工作日数 -与DateDiff的StartDate和EndDate不同 -从1月1日星期一至1月1日星期一将返回1 -============================================= 创建函数[dbo]。[ufnCountWeekdays] ( -在此处添加函数的参数 @StartDate DateTime, @EndDate DateTime ) 返回整数 如 开始 -在这里声明返回变量 DECLARE @CountofWeekDays INT = NULL; 宣告@TempDate DateTime;
-- Could CountBackwords
IF @StartDate > @ENDDate
BEGIN
SET @TempDate = @StartDate;
SET @StartDate = @EndDate;
SET @EndDate = @TempDate;
END
--Start on Weekend Never get to Monday Case
IF (DatePart(dw,@StartDate) = 7 AND DateDiff(Day,@StartDate,@EndDate) < 2)
OR (DatePart(dw,@StartDate) = 1 AND DateDiff(Day,@StartDate,@EndDate) < 1 )
BEGIN
SET @CountOfWeekDays = 0 -- Never got to a WeekDay
END
--NORMAL CASE
ELSE BEGIN
-- IF Sat Start just pretend Start Counting on Monday
IF (DatePart(dw,@StartDate) = 7) SET @StartDate = dateadd(Day, 2, @StartDate);
-- IF Sun Start just Start to Counting on Monday
IF (DatePart(dw,@StartDate) = 1) SET @StartDate = dateadd(Day, 1, @StartDate);
-- Sat End just Stop counting on Friday
IF (DatePart(dw,@EndDate) = 7) SET @EndDate = DateAdd(Day, -1, @EndDate);
-- Sun END
if (DatePart(dw,@EndDate) = 1) SET @EndDate = DateAdd(Day, -2, @EndDate);
--Find the total number of days we need to count
Declare @DaysToCount INT = DateDiff(Day,@StartDate, @EndDate)+1; --include start
-- five days for each full week plus any other weekdays
-- remember no worries about starting or ending on weekends
Set @CountofWeekDays = Floor(@DaysToCount/7)*5 + (@DaysToCount % 7)
END
--Check to see if we are counting backwards
IF @TempDate = @EndDate SET @CountofWeekDays = -1 * @CountofWeekDays;
RETURN @CountofWeekDays;
END 开始
快乐编码
Todd P Payne