简体   繁体   English

计算在日期/时间范围内超出数据集的总时间

[英]Calculating total time that falls outside of dataset within date/time range

I'm looking to add some new functionality to an existing application (database is Microsoft SQL 2005). 我希望为现有应用程序添加一些新功能(数据库是Microsoft SQL 2005)。 Basically, I'm trying to calculate how many minutes (or seconds) a particular department was "unmanned" for a particular set of date ranges. 基本上,我正在尝试计算特定部门在特定日期范围内“无人”的分钟数(或秒数)。 I'm looking to query the dataset ideally with one statement. 我想用一个语句理想地查询数据集。 I have a routine that loops through the record set, parses it and spits out an answer, but it's pretty ugly. 我有一个循环记录集的例程,解析它并吐出答案,但它非常难看。 Does anyone have any suggestions on how I can optimise it for readability, using pure SQL - or even any pointers/articles on what I should be looking at, my Googlefu is failing me. 有没有人有任何建议我如何优化它的可读性,使用纯SQL - 甚至任何关于我应该看的指针/文章,我的Googlefu让我失望。

I guess in some ways this is almost like a "free time" search of a calendar, but aggregated. 我想在某些方面,这几乎就像是对日历的“空闲时间”搜索,但是聚合了。

Here is a mock sample data set to give you an idea of what I'm working with (effectively colleagues clock in, then clock out). 这是一个模拟样本数据集,让您了解我正在使用的内容(有效的同事时钟,然后退出)。 I'm using rounding to minutes below for the sake of simplicity, but I'd likely be calculating in seconds. 为了简单起见,我正在使用四舍五入到下面的分钟,但我可能会在几秒钟内计算。

------------------------------------------------------------------------
| 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               | ...              | ...              |
------------------------------------------------------------------------

So for example, if I queried the above table for Department Id = 1, between 04/01/2010 08:30:00 and 04/01/2010 17:30:00 , I would expect a result of 35 minutes (or 2100 seconds) of "unmanned time" (this is sum of the time at the start, middle and end of the range that is unmanned). 因此,例如,如果我查询上表中的部门ID = 1,在04/01/2010 08:30:0004/01/2010 17:30:00之间 ,我预计会有35分钟(或2100)的结果秒 “无人时间”(这是无人值守范围的开始,中间和结束时间的总和)。

I have a table Integers already created, which I use for stuff like this. 我有一个已经创建的表Integers,我用它来做这样的事情。

Given that, you want: 鉴于此,您需要:

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'

It's a range intersection problem: You're looking at a number range: 这是一个范围交叉问题:你正在寻找一个数字范围:

4/01/2010 08:30:00 - 04/01/2010 17:30:00  

this range can be represented as numbers - microseconds, or seconds from beginning of the day, for example: 此范围可以表示为数字 - 从一天开始的微秒或秒,例如:

[1000000, 3000000]

and you want to find the parts of it that do not collide with any of: 并且你想找到它不会与以下任何一个碰撞的部分:

[1200000, 1250000]
[1250000, 1490000]
[1500000, 1950000]
...

When translated to number format, it would really look like this range intersection algorithm , and it can be implemented in virtually any language. 当转换为数字格式时, 它实际上看起来像这个范围交集算法 ,并且它几乎可以用任何语言实现。

Edit: 编辑:

There's a very interesting discussion about date ranges with great illustrations and explanations here . 关于日期范围有一个非常有趣的讨论, 这里有很好的插图和解释。

I'd recommend using Eric H's approach. 我建议使用Eric H的方法。 With that disclaimer out of the way, this is kinda nasty but it does offer a means of doing the same thing, if you don't have access to a numbers table for one reason or another. 有了这个免责声明,这有点令人讨厌,但如果由于某种原因无法访问数字表,它确实提供了做同样事情的方法。 I'm sure it can be improved, I just felt like trying it out w/o using the numbers table: 我确信它可以改进,我只是觉得没有使用数字表尝试它:

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

edit added the case statements above to handle calculation of StaffedTime when a particular period starts prior to @Start (or ends after @End) 编辑添加了上述案例陈述,以便在特定时期在@Start之前开始(或在@End之后结束)时处理StaffedTime的计算

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM