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

时间:2010-01-06 14:16:44

标签: sql sql-server sql-server-2005

我希望为现有应用程序添加一些新功能(数据库是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秒)的结果(这是该范围的开始,中间和结束时间的总和)无人驾驶)。

3 个答案:

答案 0 :(得分:3)

我有一个已经创建的表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'

答案 1 :(得分:1)

这是一个范围交叉问题:你正在看一个数字范围:

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

此范围可以表示为数字 - 从一天开始的微秒或秒,例如:

[1000000, 3000000]

并且您希望找到不与以下任何内容发生冲突的部分:

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

当翻译成数字格式it would really look like this range intersection algorithm时,它几乎可以用任何语言实现。

修改

有一个关于日期范围的非常有趣的讨论,有很好的插图和解释here

答案 2 :(得分:0)

我建议使用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的计算