2010-07-21 193 views
3

我需要计算数据库中事件的“活动分钟数”。开始时间是众所周知的。计算两个日期之间以分钟计的时间差(仅工作小时数)

的复杂性在于,这些活性分钟应该只在工作日期间进行计数 - 周一至周五9 am-6.30pm,不包括周末和假日天

启动或“当前”时间可(已知)名单在工作时间之外,但仍然只计算工作时间。

这是SQL Server 2005,因此可以使用T-SQL或托管程序集。

回答

4

如果你想这样做纯粹的SQL这里有一个方法

CREATE TABLE working_hours (start DATETIME, end DATETIME); 

现在填充工时表可数的时期,〜每年250行。

如果你有一个事件(@event_start,@event_end),将开始时间和结束掉小时,然后简单的查询

SELECT SUM(end-start) as duration 
FROM working_hours 
WHERE start >= @event_start AND end <= @event_end 

就足够了。

如果在另一方面,事件的开始和/或在工作时间内结束查询比较复杂

SELECT SUM(duration) 
FROM 
(
    SELECT SUM(end-start) as duration 
    FROM working_hours 
    WHERE start >= @event_start AND end <= @event_end 
UNION ALL 
    SELECT [email protected]_start 
    FROM working_hours 
    WHERE @event_start between start AND end 
UNION ALL 
    SELECT @event_end - start 
    FROM working_hours 
    WHERE @event_end between start AND end 
) AS u 

注:

  • 以上是未经测试的查询,这取决于你的RDBMS你可能需要日期/时间函数来聚合和减去日期时间(并且取决于使用的函数,上述查询可以以任何时间精度工作)。
  • 可以将查询重写为不使用UNION ALL。
  • 的working_hours表可以在系统中使用其他的东西,并允许最大的灵活性

编辑: 在MSSQL中,你可以使用DATEDIFF(MI,开始,结束)获得的分钟数为每个减法以上。

+1

+1如果计算可能会持续很多个工作日(并假定工作时间和假期相当稳定),我发现值得添加一个'julianized'工作时间列表(从任意时代到现在的总工作时间)。因此,除了UNIONing三个结果集然后进行SUM之外,您还可以对每个SUM进行相加,然后将它们加在一起,除非现在三个UNIONed结果集中的第一个的计算仅仅是减去两个值的情况(而不是求和因为它们已经被预先求和(存储不被计算),所以减去*所有*中间行)。 – onedaywhen 2010-07-21 09:29:54

+0

辉煌......超过通过托管代码解决方案的一半 - 但这更好! – 2010-07-21 15:29:00

0

在全球范围内,你需要:

  1. 办法来捕获事件的结束时间(可能通过通知,或任何开始在首位的情况下),和表格来记录这开始和结束时间。
  2. 包含所有要计数的周期(开始和结束)的助手表。 (然后你需要一些配套的代码,以保持该表最新将来)
  3. 一个存储过程,将:
    • 遍历这个帮手表并找到“主动”时期
    • 计算每个活动时段内的分钟数。

(注意,这个假设的情况下可以持续多天:是,真的有可能?)

不同的方法将有事件,检查是否每个时间里面一个滴答作响的时钟该事件应当在那个时间进行计数,并且在相关时段内每次发现自己活跃时增加(以秒或分钟为单位)。这仍然需要助手表,并且可能会更少审计(可能)。

+0

如果通过迭代你的意思是使用游标(或其他基于行的算法),那么我不同意 - 这是没有必要的(见我的答案)。 – Unreason 2010-07-21 08:55:53

1

我来这里寻找一个非常类似的问题的答案 - 我需要得到两个日期之间的分钟,不包括周末,不包括08:30和18:00以外的时间。经过一些黑客攻击后,我想我已经排序了。以下是我如何做到的。想法是欢迎 - 谁知道,也许有一天我会注册这个网站:)

create function WorkingMinutesBetweenDates(@dteStart datetime, @dteEnd datetime) 
returns int 
as 
begin 

declare @minutes int 
set @minutes = 0 

while @dteEnd>[email protected] 
    begin 

     if datename(weekday,@dteStart) <>'Saturday' and datename(weekday,@dteStart)<>'Sunday' 
      and (datepart(hour,@dteStart) >=8 and datepart(minute,@dteStart)>=30) 
      and (datepart(hour,@dteStart) <=17) 

      begin 
       set @minutes = @minutes + 1 
      end  

     set @dteStart = dateadd(minute,1,@dteStart) 
    end 

return @minutes 
end 
go 
+0

我认为这会很慢,因为它是一个循环。以上基于集合的方法可能是最好的;如果你不想使用表,那么sqlclr方法会更快。我有一个可以工作的sqlclr实现,它的速度非常快,但我们正在朝着基于集合的方法发展,以获得更多的可定义性 – 2011-04-27 09:32:48

1

我开始用什么非理性发布工作,是一个很好的开始。我测试了这是SQL Server,发现并非所有的时间都被捕获。我认为问题主要是当活动在同一天开始和结束时。该解决方案似乎是工作不够好,我

CREATE TABLE [dbo].[working_hours](
[wh_id] [int] IDENTITY(1,1) NOT FOR REPLICATION NOT NULL, 
[wh_starttime] [datetime] NULL, 
[wh_endtime] [datetime] NULL, 
PRIMARY KEY CLUSTERED 
(
[wh_id] ASC 
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF,   ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY] 
) ON [PRIMARY] 

走这剩下要做的就是填充工时表像

;WITH cte AS (
SELECT CASE WHEN DATEPART(Day,'2014-01-01 9:00:00 AM') = 1 THEN '2014-01-01 9:00:00 AM' 
ELSE DATEADD(Day,DATEDIFF(Day,0,'2014-01-01 9:00:00 AM')+1,0) END AS  myStartDate, 
CASE WHEN DATEPART(Day,'2014-01-01 5:00:00 PM') = 1 THEN '2014-01-01 5:00:00 PM' 
ELSE DATEADD(Day,DATEDIFF(Day,0,'2014-01-01 5:00:00 PM')+1,0) END AS myEndDate 
UNION ALL 
SELECT DATEADD(Day,1,myStartDate), DATEADD(Day,1,myEndDate) 
FROM cte 
WHERE DATEADD(Day,1,myStartDate) <= '2015-01-01' 
) 
INSERT INTO working_hours 
SELECT myStartDate, myEndDate 
FROM cte 
OPTION (MAXRECURSION 0) 

delete from working_hours where datename(dw,wh_starttime) IN ('Saturday', 'Sunday') 

--delete public holidays 

delete from working_hours where CAST(wh_starttime AS DATE) = '2014-01-01' 

我的第一篇文章

CREATE FUNCTION [dbo].[udFWorkingMinutes] 
(
@startdate DATETIME 
,@enddate DATETIME 
) 
RETURNS INT 
AS 
BEGIN 


DECLARE @WorkingHours INT 
SET @WorkingHours = 
(SELECT 
CASE WHEN COALESCE(SUM(duration),0) < 0 THEN 0 ELSE SUM(Duration) 
END AS Minutes 
FROM 
(
    --All whole days 
    SELECT ISNULL(DATEDIFF(mi,wh_starttime,wh_endtime),0) AS Duration 
    FROM working_hours 
    WHERE wh_starttime >= @startdate AND wh_endtime <= @enddate 
    UNION ALL 
    --All partial days where event start after office hours and finish after office hours 
    SELECT ISNULL(DATEDIFF(mi,@startdate,wh_endtime),0) AS Duration 
    FROM working_hours 
    WHERE @startdate > wh_starttime AND @enddate >= wh_endtime 
    AND (CAST(wh_starttime AS DATE) = CAST(@startdate AS DATE)) 
    AND @startdate < wh_endtime 
    UNION ALL 
    --All partial days where event starts before office hours and ends before day end 
    SELECT ISNULL(DATEDIFF(mi,wh_starttime,@enddate),0) AS Duration 
    FROM working_hours 
    WHERE @enddate < wh_endtime 
    AND @enddate >= wh_starttime 
    AND @startdate <= wh_starttime 
    AND (CAST(wh_endtime AS DATE) = CAST(@enddate AS DATE)) 
    UNION ALL 
    --Get partial day where intraday event 
    SELECT ISNULL(DATEDIFF(mi,@startdate,@enddate),0) AS Duration 
    FROM working_hours 
    WHERE @startdate > wh_starttime AND @enddate < wh_endtime 
    AND (CAST(@startdate AS DATE)= CAST(wh_starttime AS DATE)) 
    AND (CAST(@enddate AS DATE)= CAST(wh_endtime AS DATE)) 
) AS u) 

RETURN @WorkingHours 
END 
GO 

承滴盘!慈悲。

1

使用非理性的很好的起点,这里是SQL Server的一个TSQL实现2012

这第一个SQL填充表与我们的工作日期和时间,不包括周末和节假日:

declare @dteStart date 
declare @dteEnd date 
declare @dtStart smalldatetime 
declare @dtEnd smalldatetime 
Select @dteStart = '2016-01-01' 
Select @dteEnd = '2016-12-31' 

CREATE TABLE working_hours (starttime SMALLDATETIME, endtime SMALLDATETIME); 

while @dteStart <= @dteEnd 
BEGIN 
    IF datename(WEEKDAY, @dteStart) <> 'Saturday' 
    AND DATENAME(WEEKDAY, @dteStart) <> 'Sunday' 
    AND @dteStart not in ('2016-01-01' --New Years 
          ,'2016-01-18' --MLK Jr 
          ,'2016-02-15' --President's Day 
          ,'2016-05-30' --Memorial Day 
          ,'2016-07-04' --Fourth of July 
          ,'2016-09-05' --Labor Day 
          ,'2016-11-11' --Veteran's Day 
          ,'2016-11-24' --Thanksgiving 
          ,'2016-11-25' --Day after Thanksgiving 
          ,'2016-12-26' --Christmas 
         ) 
     BEGIN 
     select @dtStart = SMALLDATETIMEFROMPARTS(year(@dteStart),month(@dteStart),day(@dteStart),8,0) --8:00am 
     select @dtEnd = SMALLDATETIMEFROMPARTS(year(@dteStart),month(@dteStart),day(@dteStart),17,0) --5:00pm 
     insert into working_hours values (@dtStart,@dtEnd) 
     END 
    Select @dteStart = DATEADD(day,1,@dteStart) 
END 

现在这里是曾作为INT返回分钟的逻辑:

declare @event_start datetime2 
declare @event_end datetime2 
select @event_start = '2016-01-04 8:00' 
select @event_end = '2016-01-06 16:59' 

SELECT SUM(duration) as minutes 
FROM 
(
    SELECT DATEDIFF(mi,@event_start,@event_end) as duration 
    FROM working_hours 
    WHERE @event_start >= starttime 
    AND @event_start <= endtime 
    AND @event_end <= endtime 
UNION ALL 
    SELECT DATEDIFF(mi,@event_start,endtime) 
    FROM working_hours 
    WHERE @event_start >= starttime 
    AND @event_start <= endtime 
    AND @event_end > endtime 
UNION ALL 
    SELECT DATEDIFF(mi,starttime,@event_end) 
    FROM working_hours 
    WHERE @event_end >= starttime 
    AND @event_end <= endtime 
    AND @event_start < starttime 
UNION ALL 
    SELECT SUM(DATEDIFF(mi,starttime,endtime)) 
    FROM working_hours 
    WHERE starttime > @event_start 
    AND endtime < @event_end 
) AS u 

这正确返回1分钟害羞三个9小时工作日内

相关问题