查询以找到两次访问之间的时间窗口

时间:2018-12-30 09:23:07

标签: mysql sql database

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

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

  

working_hours
  -开始时间
  -end_time

     

访问
  -开始时间
  -结束时间
  -visit_status

值为“ 2”的visits_status被取消了访问,因此我们不包括这些访问

现在是一个小例子:

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

| 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
  •   

1 个答案:

答案 0 :(得分:0)

这看起来不像是一个直接的差距与孤岛问题。

首先是要找到足够大的访问之间的差距。
这意味着开始时间和结束时间都需要考虑。

然后,当发现这些间隙时,需要以30分钟为间隔展开。

要展开差距,您可以链接到数字表。
最好创建一个永久的。
下面的示例仅添加值,以使此答案变得简单。
但是还有其他方法。 F.e. here

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);

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

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;

“ vst”子查询通过使用变量为日期时间分配排名。

然后,“差距”子查询按排名对它们进行分组,以找到差距的开始和结束。

然后加入Working_hours。
“数字”表用于以30分钟为间隔显示时间。

结果:

start_time
08:30:00
10:40:00
14:00:00
14:30:00
15:00:00

您可以在 db <>小提琴here

上对其进行测试