[英]Calculating total time that falls outside of dataset within date/time range
我希望為現有應用程序添加一些新功能(數據庫是Microsoft SQL 2005)。 基本上,我正在嘗試計算特定部門在特定日期范圍內“無人”的分鍾數(或秒數)。 我想用一個語句理想地查詢數據集。 我有一個循環記錄集的例程,解析它並吐出答案,但它非常難看。 有沒有人有任何建議我如何優化它的可讀性,使用純SQL - 甚至任何關於我應該看的指針/文章,我的Googlefu讓我失望。
我想在某些方面,這幾乎就像是對日歷的“空閑時間”搜索,但是聚合了。
這是一個模擬樣本數據集,讓您了解我正在使用的內容(有效的同事時鍾,然后退出)。 為了簡單起見,我正在使用四舍五入到下面的分鍾,但我可能會在幾秒鍾內計算。
------------------------------------------------------------------------
| Colleague Id | Department Id | Date In | Date Out |
------------------------------------------------------------------------
| 1 | 1 | 04/01/2010 08:45 | 04/01/2010 11:45 |
| 2 | 1 | 04/01/2010 09:00 | 04/01/2010 12:15 |
| 3 | 1 | 04/01/2010 10:00 | 04/01/2010 12:00 |
| 4 | 1 | 04/01/2010 12:30 | 04/01/2010 17:00 |
| 1 | 1 | 04/01/2010 12:45 | 04/01/2010 17:15 |
| 3 | 1 | 04/01/2010 13:00 | 04/01/2010 17:25 |
| 5 | 2 | ... | ... |
------------------------------------------------------------------------
因此,例如,如果我查詢上表中的部門ID = 1,在04/01/2010 08:30:00和04/01/2010 17:30:00之間 ,我預計會有35分鍾(或2100)的結果秒 “無人時間”(這是無人值守范圍的開始,中間和結束時間的總和)。
我有一個已經創建的表Integers,我用它來做這樣的事情。
鑒於此,您需要:
drop table foo
go
create table foo (
c_id int not null,
d_id int not null,
datein datetime not null,
dateout datetime not null
)
go
insert into foo values (1, 1, '04/01/2010 08:45', '04/01/2010 11:45')
insert into foo values (2, 1, '04/01/2010 09:00', '04/01/2010 12:15')
insert into foo values (3, 1, '04/01/2010 10:00', '04/01/2010 12:00')
insert into foo values (4, 1, '04/01/2010 12:30', '04/01/2010 17:00')
insert into foo values (1, 1, '04/01/2010 12:45', '04/01/2010 17:15')
insert into foo values (3, 1, '04/01/2010 13:00', '04/01/2010 17:25')
go
drop procedure unmanned
go
create procedure unmanned
@d_id int,
@start datetime,
@end datetime
as
select distinct dateadd(ss,i_int,@start)
from Integers
left join foo on dateadd(ss,i_int,@start) >= datein and dateadd(ss,i_int,@start) < dateout
where i_int between 0 and 60*60*24
and dateadd(ss,i_int,@start) >= @start and dateadd(ss,i_int,@start)< @end
and datein is null
order by 1
go
exec unmanned 1, '4/1/10 8:30', '4/1/10 17:30'
這是一個范圍交叉問題:你正在尋找一個數字范圍:
4/01/2010 08:30:00 - 04/01/2010 17:30:00
此范圍可以表示為數字 - 從一天開始的微秒或秒,例如:
[1000000, 3000000]
並且你想找到它不會與以下任何一個碰撞的部分:
[1200000, 1250000]
[1250000, 1490000]
[1500000, 1950000]
...
當轉換為數字格式時, 它實際上看起來像這個范圍交集算法 ,並且它幾乎可以用任何語言實現。
編輯:
關於日期范圍有一個非常有趣的討論, 這里有很好的插圖和解釋。
我建議使用Eric H的方法。 有了這個免責聲明,這有點令人討厭,但如果由於某種原因無法訪問數字表,它確實提供了做同樣事情的方法。 我確信它可以改進,我只是覺得沒有使用數字表嘗試它:
Declare @Start DateTime, @End DateTime
Select @Start = '04/01/2010 09:30'
, @End = '04/01/2010 17:30'
--Table Creation Stuff
Declare @y Table (ColleagueId Int, DepartmentId Int, DateIn DateTime, DateOut DateTime)
Insert @y
Select 1, 1, '04/01/2010 08:45' , '04/01/2010 11:45'
Union All Select 2 , 1, '04/01/2010 09:00' , '04/01/2010 12:15'
Union All Select 3 , 1, '04/01/2010 10:00' , '04/01/2010 12:00'
Union All Select 4 , 1, '04/01/2010 12:30' , '04/01/2010 17:00'
Union All Select 1 , 1, '04/01/2010 12:45' , '04/01/2010 17:15'
Union All Select 3 , 1, '04/01/2010 13:00' , '04/01/2010 17:25'
---------
Select DateDiff(minute, @Start, @End) -- TotalTime
- Sum(DateDiff(minute,
Case When DateIn < @Start Then @Start Else DateIn End,
Case When DateOut > @End Then @End Else DateOut End)) --StaffedTime
as UnmannedTime
From
(
Select Min(din) DateIn, dout DateOut
From
(
Select Min(y.DateIn) din, Max(y2.DateOut) dout
From @y y
Inner Join @y y2 on y.DateOut >= y2.DateIn
--you probably want to close the other end of these filters, but leave some room
--(to handle the guy who started @ 7:45, etc...)
Where y.DateIn < @End
and y2.DateOut > @Start
Group By y.DateIn
) x
Group By dout
) q
編輯添加了上述案例陳述,以便在特定時期在@Start之前開始(或在@End之后結束)時處理StaffedTime的計算
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.