使用SQL中的每日限制计算持续时间

时间:2014-09-12 08:52:13

标签: sql sql-server date datetime

我有一个表格,用于存储数据库中的一些事件,记录机器的运行时间,我想计算特定班次(或所有轮班)特定日期的总运行时间。

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

我真的很想拥有一个基于集合的解决方案,因为数据集相当大,并且考虑将循环的基于游标的解决方案作为最后的手段。

1 个答案:

答案 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