Mysql 查询以查找访问之间的时间窗口

Mysql 查询以查找访问之间的时间窗口,mysql,sql,database,Mysql,Sql,Database,我正在创建一个小应用程序来注册访问,我一直在寻找访问之间的空闲时间窗口 有两个表,这是一个简化的结构: 工作时间 -开始时间 -结束时间 拜访 -开始时间 -结束时间 -探访 值为2的访问状态为取消访问,因此我们不包括这些访问 现在举个小例子: 员工有自己的工作时间,包括休息时间,例如: | start_time | end_time | | 2018-12-29 08:00:00 | 2018-12-29 12:00:00 | | 2018-12-29

我正在创建一个小应用程序来注册访问,我一直在寻找访问之间的空闲时间窗口

有两个表,这是一个简化的结构:

工作时间 -开始时间 -结束时间

拜访 -开始时间 -结束时间 -探访

值为2的访问状态为取消访问,因此我们不包括这些访问

现在举个小例子:

员工有自己的工作时间,包括休息时间,例如:

| start_time          | end_time            |
| 2018-12-29 08:00:00 | 2018-12-29 12:00:00 |
| 2018-12-29 12:30:00 | 2018-12-29 16:00:00 |
已在应用中注册了访问,它们可能有不同的持续时间。参观时间已包括休息时间,因此下次参观可在休息后立即开始。假设我们有这样的访问:

| start_time          | end_time            | visit_status |
| 2018-12-29 08:00:00 | 2018-12-29 08:30:00 | 1            |
| 2018-12-29 09:00:00 | 2018-12-29 10:00:00 | 1            |
| 2018-12-29 10:00:00 | 2018-12-29 10:40:00 | 1            |
| 2018-12-29 10:40:00 | 2018-12-29 11:10:00 | 2            |
| 2018-12-29 11:10:00 | 2018-12-29 11:40:00 | 0            |
| 2018-12-29 12:30:00 | 2018-12-29 13:00:00 | 0            |
| 2018-12-29 13:00:00 | 2018-12-29 14:00:00 | 0            |
| 2018-12-29 15:30:00 | 2018-12-29 16:00:00 | 0            |
我的目标是创建一个查询,显示30分钟访问的可用开始时间(包括工作时间),在这种特定情况下,结果应为以下时间:

8:30 10:40 14:00 14:30 15:00
这看起来不像是一个简单的缺口和孤岛问题

首先有一个问题,就是要找出访问之间的差距是否足够大。 这意味着需要同时考虑开始和结束时间

然后,当发现这些间隙时,它们需要在30分钟的间隔内展开

要展开间隙,可以链接到数字表。 最好创建一个永久的。 下面的示例只是添加了一些值,以使这个答案保持简单。 但还有其他方法。F.e。

这样的查询将在MySql 5.7中工作


您可以在dbfiddle上测试它

此查询不包括14:00、14:30、15:00等小时数。这仍然是预订30分钟的正确时间visit@kamcik现在它找到了缺口,就像一个符咒!非常感谢。我应该改变什么来找出40分钟访问或任何其他时间间隔的间隔?查看HAVING中的第二个标准并了解它的作用。关于时间间隔的展开,看看num是在哪里使用的。因为它是每个员工的,我们可以假设在工作时间里有类似emp_id的东西吗?那么在访问中也是这样吗?你基本上是在寻找访问中的差距?属性emp_id不相关,我们可以假设有一名员工。正如我在描述中提到的,我希望在已预订的访问之间为新访问找到30分钟的时间窗口。有一次访问在14:00:00结束,另一次预订的访问在15:30开始,所以我可以在3个不同的时间预订一次新的访问-14:00,14:30,15:00。@Kamcik。为什么你不能在14:15或14:50预订?浪费时间-在14:00和15:30之间,你可以预订三次30分钟的访问,当你在14:15预订访问时,你在14:00和14:15之间浪费时间,你只能预订两次访问14:15和14:45,这都是因为下一个时间窗口是15:15,没有时间进行30分钟的访问。
create table nums (num int primary key not null);
insert into nums (num) VALUES 
(00),(01),(02),(03),(04),(05),(06),(07),(08),(09),
(10),(11),(12),(13),(14),(15),(16),(17),(18),(19),
(20),(21),(22),(23),(24),(25),(26),(27),(28),(29),
(30),(31),(32),(33),(34),(35),(36),(37),(38),(39),
(40),(41),(42),(43),(44),(45),(46),(47),(48),(49);
SELECT DISTINCT 
 CAST(gaps.start_dt + INTERVAL (nums.num * 30) MINUTE AS TIME) as start_time
FROM
(
  SELECT rnk, MIN(prev_dt) as start_dt, MIN(start_dt) as end_dt
  FROM
  (
    SELECT 
    start_time AS start_dt, 
    end_time as end_dt, 
    @prev_dt as prev_dt,
    -- DATE(@prev_dt) + INTERVAL (CEIL(TIME_TO_SEC(@prev_dt) / 600) * 600) SECOND as prev_dt,
    (CASE
     WHEN @prev_dt = start_time AND @prev_dt := end_time THEN @rnk
     WHEN @prev_dt := end_time THEN @rnk := @rnk + 1
     END) AS rnk
    FROM visits
    CROSS JOIN (SELECT @prev_dt := null, @rnk := 0) vars
    WHERE visit_status <> 2
    ORDER BY start_time
  ) AS vst
  GROUP BY rnk
  HAVING CAST(MIN(start_dt) AS DATE) = CAST(MIN(prev_dt) AS DATE)
     AND TIMEDIFF(MIN(start_dt), MIN(prev_dt)) >= CAST('00:30' AS TIME)
) gaps
JOIN working_hours wrk 
  ON wrk.start_time <= gaps.start_dt AND wrk.end_time >= gaps.end_dt
JOIN nums
  ON nums.num BETWEEN 0 AND 47
 AND gaps.start_dt + INTERVAL (nums.num * 30) MINUTE < gaps.end_dt;
start_time
08:30:00
10:40:00
14:00:00
14:30:00
15:00:00