2010-07-21 197 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小時工作日內

相關問題