MySql 查询返回时间大于 22:00 且小于 06:00 一组不同的时间戳

A MySql query for returning time which is greater than 22:00 and smaller than 06:00 for group of different timestamps

我正在开发一个工作时间注册器,我想知道是否有人有一个简单的解决方案来从时间戳数组中选择 "night hours",它代表工作的开始和结束(当然这可能是过夜)。

示例时间戳:

SELECT start, end FROM work

start                   end
2016-05-23 18:13:59     2016-05-24 02:12:45
2016-05-24 18:12:47     2016-05-25 02:13:39
2016-05-25 17:39:28     2016-05-26 01:37:35
2016-05-26 17:39:42     2016-05-27 01:39:31
2016-05-30 01:59:43     2016-05-30 10:41:37
2016-05-31 01:55:23     2016-05-31 10:49:11
2016-06-01 02:01:21     2016-06-01 12:03:13

我想 return 总时间(在一个字段中),即在 22:00:00 之后和 06:00:00 之前 - 所以是晚上。有任何想法吗?谢谢。

这假设信息来自您在问题 maximum set of night hours is always 1. Hope this would simplify things a bit. 下的评论,并在下面稍作扩展。它允许工人在每个班次的 2 个日历日块中打多达 3 个夜间时间块。这些由 3 个范围表示。如果轮班的总工作时间超过 2 个日历日,则可以轻松扩展它。但是请注意,示例数据的最后一行是 41 小时的长班次。只问是否需要如何扩展它。但相同的数据显示了各种边界条件测试,希望现在涵盖所有内容。

架构

create table times
(   id int auto_increment primary key,
    startDT datetime not null,
    endDT datetime not null
);

示例数据

insert times(startDT,endDT) values
('2016-05-23 18:13:59','2016-05-24 02:12:45'),
('2016-05-24 18:12:47','2016-05-25 02:13:39'),
('2016-05-25 17:39:28','2016-05-26 01:37:35'),
('2016-05-26 17:39:42','2016-05-27 01:39:31'),
('2016-05-30 01:59:43','2016-05-30 10:41:37'),
('2016-05-31 01:55:23','2016-05-31 10:49:11'),
('2016-06-01 02:01:21','2016-06-01 12:03:13'),
('2016-06-01 05:30:00','2016-06-01 13:00:00'),
('2016-06-01 05:30:00','2016-06-01 22:35:00'),
('2016-06-01 05:30:00','2016-06-01 22:30:00'),
('2016-06-01 05:30:00','2016-06-02 22:30:00');

显示调试信息的查询

select id,startDt,endDt,
coalesce(@r1Begin:=concat(date(startDt),' 00:00:00'),null) as `@r1Begin`,
coalesce(@r1End:=concat(date(startDt),' 06:00:00'),null) as `@r1End`,
coalesce(@r2Begin:=concat(date(startDt),' 22:00:00'),null) as `@r2Begin`,
coalesce(@r2End:=concat(date(date_add(date(startDt),interval 1 day)), ' 06:00:00'),null) as `@r2End`,
coalesce(@r3Begin:=concat(date(date_add(date(startDt),interval 1 day)), ' 22:00:00'),null) as `@r3Begin`,
coalesce(@r3End:=concat(date(date_add(date(startDt),interval 1 day)), ' 23:59:59'),null) as `@r3End`,
(secondsOverlapped(startDt,endDt,@r1Begin,@r1End) + secondsOverlapped(startDt,endDt,@r2Begin,@r2End) + secondsOverlapped(startDt,endDt,@r3Begin,@r3End) ) / 3600 as graveyardHours
from times;

上面放大到只有 4 列

+----+---------------------+---------------------+----------------+
| id | startDt             | endDt               | graveyardHours |
+----+---------------------+---------------------+----------------+
|  1 | 2016-05-23 18:13:59 | 2016-05-24 02:12:45 |         4.2125 |
|  2 | 2016-05-24 18:12:47 | 2016-05-25 02:13:39 |         4.2275 |
|  3 | 2016-05-25 17:39:28 | 2016-05-26 01:37:35 |         3.6264 |
|  4 | 2016-05-26 17:39:42 | 2016-05-27 01:39:31 |         3.6586 |
|  5 | 2016-05-30 01:59:43 | 2016-05-30 10:41:37 |         4.0047 |
|  6 | 2016-05-31 01:55:23 | 2016-05-31 10:49:11 |         4.0769 |
|  7 | 2016-06-01 02:01:21 | 2016-06-01 12:03:13 |         3.9775 |
|  8 | 2016-06-01 05:30:00 | 2016-06-01 13:00:00 |         0.5000 |
|  9 | 2016-06-01 05:30:00 | 2016-06-01 22:35:00 |         1.0833 |
| 10 | 2016-06-01 05:30:00 | 2016-06-01 22:30:00 |         1.0000 |
| 11 | 2016-06-01 05:30:00 | 2016-06-02 22:30:00 |         9.0000 |
+----+---------------------+---------------------+----------------+

id 11 是一个 41 小时的班次,涉及 3 个灰场小时段,但只占 9 个灰场小时:1/2 小时 + 8 小时 + 1/2 小时

1 行 1 列的最终查询

select sum(graveyardShiftHoursWorked) as graveYardHours
from
(   select id,startDt,endDt,
    coalesce(@r1Begin:=concat(date(startDt),' 00:00:00'),null) as `@r1Begin`,
    coalesce(@r1End:=concat(date(startDt),' 06:00:00'),null) as `@r1End`,
    coalesce(@r2Begin:=concat(date(startDt),' 22:00:00'),null) as `@r2Begin`,
    coalesce(@r2End:=concat(date(date_add(date(startDt),interval 1 day)), ' 06:00:00'),null) as `@r2End`,
    coalesce(@r3Begin:=concat(date(date_add(date(startDt),interval 1 day)), ' 22:00:00'),null) as `@r3Begin`,
    coalesce(@r3End:=concat(date(date_add(date(startDt),interval 1 day)), ' 23:59:59'),null) as `@r3End`,
    (secondsOverlapped(startDt,endDt,@r1Begin,@r1End) + secondsOverlapped(startDt,endDt,@r2Begin,@r2End) + secondsOverlapped(startDt,endDt,@r3Begin,@r3End) ) / 3600 as graveyardShiftHoursWorked
    from times
) xDerived;

+----------------+
| graveyardHours |
+----------------+
|        39.3674 |
+----------------+

使用的函数

以下是一个用户定义的函数,它采用工作日期时间的开始和结束时间,并确定与提供的范围重叠的秒数以进行比较。我把它留作 reader 的练习,以美化它,以便解决所有错误陷阱。例如,如果提供的工作日范围有工作人员的结束 datetime 发生在开始 datetime 之前(即:您的数据不正确),等等

注意,函数returns seconds。正是在查询本身中使用此函数,它除以 3600 以转换为小时数。

drop function if exists secondsOverlapped;
DELIMITER $$
create function secondsOverlapped(r1Begin datetime,r1End datetime,r2Begin datetime,r2End datetime)
RETURNS int DETERMINISTIC
BEGIN
    DECLARE beginOverride datetime;
    DECLARE endOverride datetime;
    DECLARE elapsedSeconds int; 

    IF (r1End<=r2Begin) or (r2End<=r1Begin) THEN
        return 0;
    END IF;
    set beginOverride=greatest(r1Begin,r2Begin);
    set endOverride=least(r1End,r2End);
    set elapsedSeconds=TIME_TO_SEC(TIMEDIFF(endOverride,beginOverride));
    return elapsedSeconds;
END$$
DELIMITER ;

Mysql CREATE PROCEDURE and CREATE FUNCTION and Least and Greatest functions

的手册页