这与此处发现的问题类似......
Count work days between two dates
最大的区别在于我需要以小时为单位计算我的范围,我需要考虑部分天数,其中一些可能是周末。例如,我看到的大多数代码示例都是周六和周日整体,就像这样可以返回该范围内的非周末天数...
SELECT @WeekDays =
(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)
我需要这样的东西,但就小时而言。大多数论坛/解决方案会让我将周末日数增加24倍,如果我输入“星期六中午”到“星期一下午6点”之类的东西,那就不行了。我将从总小时数减去48小时而不是36小时。
为了记录,这是我现在正在做的,以便在范围内找到所有小时...
SELECT @HourCount = DATEDIFF(hh, @StartDate, @EndDate)
我愿意在一次移动中正确计算它,或者使用上面的@HourCount并减去范围内的“周末小时数”。只是不确定如何准确地做任何一个。
最后请注意,所有重要的效率事情。从技术上讲,我可能会使用while循环并从@StartDate迭代到@EndDate,同时增加小时(甚至分钟),检查当前日期,如果不是周末,则将总数加1。这可能是我最糟糕的情况,但我正在寻找一种比这更有效的解决方案。欢迎任何建议或解决方案。
答案 0 :(得分:0)
未经详尽测试,但有一个版本;
WITH cte AS (
SELECT start_time, end_time,
(@@datefirst + datepart(weekday, start_time)) % 7 start_day,
(@@datefirst + datepart(weekday, end_time )) % 7 end_day
FROM test
), cte2 AS (
SELECT CASE WHEN start_day < 2 THEN CAST(start_time + 2 - start_day AS DATE)
ELSE start_time END start_time,
CASE WHEN end_day < 2 THEN CAST(end_time - end_day AS DATE)
ELSE end_time END end_time
FROM cte
)
SELECT DATEDIFF(hh, start_time, end_time) -
DATEDIFF(wk, start_time, end_time) * 48 diff
FROM cte2;
第一个公用表表达式获取星期几,0表示星期六,1表示星期日等开始和结束时间。
第二个公用表格表达式将开始和结束时间调整为永远不会在周末(开始时间向前移动到星期一,结束时间回到星期五(或者说午夜到星期六)。
查询本身只是计算时间之间的差异,调整周末。
我可以在查询中看到的唯一明显问题是:
如果开始和结束都在同一个周末,则返回的时间将为负数,因此您可能需要添加逻辑来处理该问题。
如果某个区域设置考虑周中某周的开始,则可能会计算周末错误,因此在这种情况下您可能需要对此进行调整。
答案 1 :(得分:0)
考虑为此查询和其他日期/时间查询创建永久日历表和实用程序编号表。这将允许您使用有效的基于集合的查询,并在除周末之外的非工作日帐户。有关创建和加载这些表的示例脚本,请参阅http://www.dbdelta.com/calendar-table-and-datetime-functions/。
下面是一个示例查询,说明了这些表的一种方法,您可以根据需要进行调整。
DECLARE
@StartDate datetime = '2014-07-12 12:00:00'
,@EndDate datetime = '2024-07-14 18:00:00';
WITH
business_days AS (
SELECT CalendarDate
FROM dbo.Calendar
WHERE
CalendarDate BETWEEN CAST(@StartDate AS date) AND DATEADD(day, 1, @EndDate)
AND BusinessDay = 1
)
,business_hours AS (
SELECT DATEADD(hour, Number, CAST(CalendarDate AS datetime)) AS BusinessHour
FROM business_days
CROSS JOIN dbo.Numbers
WHERE Number BETWEEN 0 AND 23
)
SELECT COUNT(*) AS total_business_hours
FROM business_hours
WHERE
BusinessHour >= @StartDate
AND BusinessHour < @EndDate;