对于时间跨度组,我确实遇到了棘手的问题。
例如: 在一家大公司,一辆汽车发生故障并被提交给内部机械团队。我确实有时间,提交和发布时间。我在寻找什么,车库中的车有多长时间可以使用它?
所以我有两张桌子。
1。)一个表@e,包含eventstart和endtimes。 2.)一张桌子@o每个工作日的办公时间。工作日从星期一的1开始。
我需要的是在办公时间内增加的时间。
我制作了一个包含样本数据的集合,以便于测试:
declare @o table ([id] [int], [weekday] [smallint], [starttime] [time](7), [endtime] [time](7))
insert into @o([id], [weekday], [starttime], [endtime]) values
(1, 1, CAST(N'09:00:00' AS Time), CAST(N'17:00:00' AS Time)),
(2, 2, CAST(N'09:00:00' AS Time), CAST(N'17:00:00' AS Time)),
(3, 3, CAST(N'09:00:00' AS Time), CAST(N'17:00:00' AS Time)),
(4, 4, CAST(N'09:00:00' AS Time), CAST(N'17:00:00' AS Time)),
(5, 5, CAST(N'09:00:00' AS Time), CAST(N'14:00:00' AS Time)),
(6, 6, CAST(N'12:00:00' AS Time), CAST(N'15:00:00' AS Time)),
(7, 7, CAST(N'12:00:00' AS Time), CAST(N'15:00:00' AS Time))
declare @e table ( [startspan] [datetime], [endspan] [datetime] )
INSERT INTO @e ([startspan], [endspan]) VALUES
(CAST(N'2015-05-06 15:08:59' AS DateTime), CAST(N'2015-05-13 09:52:09' AS DateTime)),
(CAST(N'2015-05-05 20:46:17' AS DateTime), CAST(N'2015-05-08 11:34:50' AS DateTime)),
(CAST(N'2015-05-02 14:42:23' AS DateTime), CAST(N'2015-05-05 17:22:30' AS DateTime)),
(CAST(N'2015-05-01 09:07:36' AS DateTime), CAST(N'2015-05-04 08:31:35' AS DateTime)),
(CAST(N'2015-05-01 00:16:00' AS DateTime), CAST(N'2015-05-04 12:58:27' AS DateTime)),
(CAST(N'2015-04-30 19:14:25' AS DateTime), CAST(N'2015-05-05 20:29:48' AS DateTime)),
(CAST(N'2015-04-24 12:48:34' AS DateTime), CAST(N'2015-04-27 16:15:22' AS DateTime)),
(CAST(N'2015-04-22 13:05:29' AS DateTime), CAST(N'2015-04-27 11:13:28' AS DateTime)),
(CAST(N'2015-04-18 11:01:17' AS DateTime), CAST(N'2015-04-20 15:44:41' AS DateTime)),
(CAST(N'2015-04-18 09:49:51' AS DateTime), CAST(N'2015-04-20 12:18:42' AS DateTime))
我目前的方法是将所有带有偶数的事件分成带有开始和结束的单日,然后计算每个事件和组的时间,然后计算时间和时间。 这将是一个很大的sql负载。如果有人有不同的方法或任何想法,我们非常感激。
为了避免这个问题:我使用MS SQL-Server 2014,我有大约1000万个事件。
感谢您提供任何帮助或想法。
Metanormal
答案 0 :(得分:0)
在我看来,像这样的简单查询就可以解决问题
;with alldates as
(
select convert(datetime, '2015-04-01') as d
union all select DATEADD(day, 1, d) from alldates where d < '2015-06-01'
),
oh as
(
select alldates.d
, alldates.d + CONVERT(datetime, #o.starttime) as starttime
, alldates.d + CONVERT(datetime, #o.endtime) as endtime
,datediff(minute, alldates.d + CONVERT(datetime, #o.starttime), alldates.d + CONVERT(datetime, #o.endtime)) as tothours
from alldates inner join #o on DATEPART(weekday, alldates.d) = #o.id
)
select #e.[startspan], #e.[endspan],
SUM(
datediff(minute,
case when cast(#e.[startspan] as date)=oh.d then #e.[startspan] else oh.starttime end,
case when cast(#e.[endspan] as date)=oh.d then #e.[endspan] else oh.endtime end)
) as totminutes
from oh
inner join #e on cast(#e.[startspan] as date) <= oh.d and cast(#e.[endspan] as date) >= oh.d
--where #e.startspan = '2015-05-06 15:08:59'
group by #e.[startspan], #e.[endspan]
order by 1
有一个限制。您应该将alldates CTE的开始和结束日期定义为系统中最早和最晚的事件日期。对于大的数据,你应该考虑创建一个虚拟的alldates表,其中包含alldates,甚至更好,创建类似于oh CTE的整个日历表。这样你就需要使用最后的SELECT来获得所需的结果。
结果
startspan endspan totminutes
----------------------- ----------------------- -----------
2015-04-18 09:49:51.000 2015-04-20 12:18:42.000 989
2015-04-18 11:01:17.000 2015-04-20 15:44:41.000 1123
2015-04-22 13:05:29.000 2015-04-27 11:13:28.000 1508
2015-04-24 12:48:34.000 2015-04-27 16:15:22.000 1227
2015-04-30 19:14:25.000 2015-05-05 20:29:48.000 1695
2015-05-01 00:16:00.000 2015-05-04 12:58:27.000 1782
2015-05-01 09:07:36.000 2015-05-04 08:31:35.000 984
2015-05-02 14:42:23.000 2015-05-05 17:22:30.000 1480
2015-05-05 20:46:17.000 2015-05-08 11:34:50.000 528
2015-05-06 15:08:59.000 2015-05-13 09:52:09.000 2264
(10 row(s) affected)
答案 1 :(得分:0)
很多不同于CHA和其他输入。
cha的答案几乎是完美的,我不得不用消极的时间来表示一些特价。并且有工作日和id的列拼写错误。
现在它运行得很好。
我为他们最初拥有的事件添加了一个id(当然; - )。
以下是完整的工作示例代码:
declare @o table ([oid] [int], [weekday] [smallint], [starttime] [time](7), [endtime] [time](7))
insert into @o([oid], [weekday], [starttime], [endtime]) values
(1, 1, CAST(N'09:00:00' AS Time), CAST(N'17:00:00' AS Time)),
(2, 2, CAST(N'09:00:00' AS Time), CAST(N'17:00:00' AS Time)),
(3, 3, CAST(N'09:00:00' AS Time), CAST(N'17:00:00' AS Time)),
(4, 4, CAST(N'09:00:00' AS Time), CAST(N'17:00:00' AS Time)),
(5, 5, CAST(N'09:00:00' AS Time), CAST(N'14:00:00' AS Time)),
(6, 6, CAST(N'12:00:00' AS Time), CAST(N'15:00:00' AS Time)),
(7, 7, CAST(N'12:00:00' AS Time), CAST(N'15:00:00' AS Time))
declare @e table ( [eid] [int], [startspan] [datetime], [endspan] [datetime] )
INSERT INTO @e ([eid], [startspan], [endspan]) VALUES
(1,CAST(N'2015-05-06 15:08:59' AS DateTime), CAST(N'2015-05-13 09:52:09' AS DateTime)),
(2,CAST(N'2015-05-05 20:46:17' AS DateTime), CAST(N'2015-05-08 11:34:50' AS DateTime)),
(3,CAST(N'2015-05-02 14:42:23' AS DateTime), CAST(N'2015-05-05 17:22:30' AS DateTime)),
(4,CAST(N'2015-05-01 09:07:36' AS DateTime), CAST(N'2015-05-04 08:31:35' AS DateTime)),
(5,CAST(N'2015-05-01 00:16:00' AS DateTime), CAST(N'2015-05-04 12:58:27' AS DateTime)),
(6,CAST(N'2015-04-30 19:14:25' AS DateTime), CAST(N'2015-05-05 20:29:48' AS DateTime)),
(7,CAST(N'2015-04-24 12:48:34' AS DateTime), CAST(N'2015-04-27 16:15:22' AS DateTime)),
(8,CAST(N'2015-04-22 13:05:29' AS DateTime), CAST(N'2015-04-27 11:13:28' AS DateTime)),
(9,CAST(N'2015-04-18 11:01:17' AS DateTime), CAST(N'2015-04-20 15:44:41' AS DateTime)),
(10,CAST(N'2015-04-18 09:49:51' AS DateTime), CAST(N'2015-04-20 12:18:42' AS DateTime))
set datefirst 1
;with alldates as
(
select convert(datetime, '2015-01-01') as d
union all select DATEADD(day, 1, d) from alldates where d < '2015-07-01'
) , oh
as
(
select alldates.d
, alldates.d + CONVERT(datetime, o.[starttime]) as starttime
, alldates.d + CONVERT(datetime, o.endtime) as endtime
,datediff(minute, alldates.d + CONVERT(datetime, o.starttime), alldates.d + CONVERT(datetime, o.endtime)) as tothours
from alldates inner join @o as o on DATEPART(weekday, alldates.d) = o.[weekday]
)
select e.eid, e.[startspan], e.[endspan]
,sum(
datediff(minute,
case when cast(e.[startspan] as date)=oh.d
then
case when e.[startspan] < starttime
then starttime
else
case when e.[startspan] < endtime
then e.[startspan]
else endtime
end
end
else
oh.starttime
end,
case when cast(e.[endspan] as date)=oh.d
then
case when e.[endspan] < endtime
then
case when e.[endspan] < starttime
then starttime
else e.[endspan]
end
else
e.[endspan]
end
else
oh.endtime
end) ) totminutes
from oh
inner join @e as e on cast(e.[startspan] as date) <= oh.d and cast(e.[endspan] as date) >= oh.d
--where e.startspan = '2015-05-01 09:07:36.000'
group by e.[eid], e.[startspan], e.[endspan]
order by e.[eid]
option (maxrecursion 0)