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