我有一个表格,用于存储数据库中的一些事件,记录机器的运行时间,我想计算特定班次(或所有轮班)特定日期的总运行时间。
CREATE TABLE events (
ID int,
StartTime datetime,
EndTime datetime,
DurationSeconds bigint,
...
)
我想在考虑每日轮班(@dateStart datetime, @dateEnd datetime
)的同时选择指定日期范围@shiftStart time, @shiftEnd time
内的事件总持续时间。这些事件将与班次和日期范围重叠。
例如,如果我从6:00开始到12:00结束,并且活动持续了整整2天(2014/01/01 00:00 - 2014/01/03 00:00),这一行的总时间是(48小时 - 2 * 6小时= 36小时)。
如果一个事件在班次中间开始,那么只有“在班次”中。部分应该考虑。
到目前为止,我有一个实现,没有考虑像:
这样的转变select sum(
--duration minus start overlap and end overlap
duration - dbo.udf_max(datediff(s,@pTo,endtime),0) - dbo.udf_max(datediff(s,starttime,@pFrom),0)
)
from events
where starttime < @pTo and endtime > @pFrom
我真的很想拥有一个基于集合的解决方案,因为数据集相当大,并且考虑将循环的基于游标的解决方案作为最后的手段。
答案 0 :(得分:1)
好吧,让我们制作一些测试数据(我改变了一点时间以显示差异)
DECLARE @Events TABLE
(
ID int IDENTITY (1, 1),
StartTime datetime,
EndTime datetime,
DurationSeconds bigint
)
INSERT INTO @Events
( StartTime, EndTime )
VALUES
( '2014/01/01 01:00', '2014/01/02 22:00');
DECLARE @Shift TABLE
(
ShiftName VARCHAR(20),
StartTime DATETIME,
EndTime DATETIME
)
INSERT INTO @Shift
( ShiftName, StartTime, EndTime )
VALUES
( 'Night', '2014/01/01 00:00', '2014/01/01 06:00' ),
( 'Morning', '2014/01/01 06:00', '2014/01/01 12:00' ),
( 'Afternoon', '2014/01/01 12:00', '2014/01/01 18:00' ),
( 'Evening', '2014/01/01 18:00', '2014/01/02 00:00' ),
( 'Night', '2014/01/02 00:00', '2014/01/02 06:00' ),
( 'Morning', '2014/01/02 06:00', '2014/01/02 12:00' ),
( 'Afternoon', '2014/01/02 12:00', '2014/01/02 18:00' ),
( 'Evening', '2014/01/02 18:00', '2014/01/03 00:00' );
在这里,我创建一个数字表来查找事件持续时间的所有分钟
DECLARE @StartDate DATETIME = '1/1/2014';
DECLARE @number_of_numbers INT = 100000;
;WITH
a AS (SELECT 1 AS i UNION ALL SELECT 1),
b AS (SELECT 1 AS i FROM a AS x, a AS y),
c AS (SELECT 1 AS i FROM b AS x, b AS y),
d AS (SELECT 1 AS i FROM c AS x, c AS y),
e AS (SELECT 1 AS i FROM d AS x, d AS y),
f AS (SELECT 1 AS i FROM e AS x, e AS y),
numbers AS
(
SELECT TOP(@number_of_numbers)
ROW_NUMBER() OVER (ORDER BY (SELECT NULL)) AS number
FROM f
), mins_in_day AS
现在我发现每个班次的所有分钟工作时间总计
(
SELECT DATEADD(MINUTE, n.number, @StartDate) AS DayMinute
FROM numbers n
), output AS
(
SELECT s.ShiftName, CONVERT(DATE, s.StartTime) ShiftDay, COUNT(1) AS TotalMinutes FROM @Shift s
INNER JOIN mins_in_day sc
ON sc.DayMinute >= s.StartTime AND sc.DayMinute < s.EndTime
INNER JOIN @Events e
ON sc.DayMinute >= e.StartTime AND sc.DayMinute <e.EndTime
GROUP BY s.ShiftName, s.StartTime
)
SELECT * FROM output
这是输出:
ShiftName ShiftDay TotalMinutes
Night 2014-01-01 300
Morning 2014-01-01 360
Afternoon 2014-01-01 360
Evening 2014-01-01 360
Night 2014-01-02 360
Morning 2014-01-02 360
Afternoon 2014-01-02 360
Evening 2014-01-02 240