Mysql:找到两个日期和时间之间的空时间块?

时间:2014-05-18 10:55:25

标签: mysql

我想从数据库表中找出用户的可用性:

primary id | UserId | startdate        | enddate
1          | 42     | 2014-05-18 09:00 | 2014-05-18 10:00
2          | 42     | 2014-05-18 11:00 | 2014-05-18 12:00
3          | 42     | 2014-05-18 14:00 | 2014-05-18 16:00
4          | 42     | 2014-05-18 18:00 | 2014-05-18 19:00

让我们考虑上面插入的数据是用户的忙碌时间,我想在开始时间和结束时间之间找出表中的空闲时间间隔块。

BETWEEN 2014-05-18 11:00 AND 2014-05-18 19:00;

让我在这里添加表格模式以避免混淆:

Create Table availability (
  pid int not null,
  userId int not null,
  StartDate datetime,
  EndDate datetime
);

Insert Into availability values
  (1, 42, '2013-10-18 09:00', '2013-10-18 10:00'),
  (2, 42, '2013-10-18 11:00', '2013-10-18 12:00'),
  (3, 42, '2013-10-18 14:00', '2013-11-18 16:00'),
  (4, 42, '2013-10-18 18:00', '2013-11-18 19:00');

需要量: 我想找出像这样的免费差距记录:

'2013-10-27 10:00' to '2013-10-28 11:00' - User is available for 1 hours and    
'2013-10-27 12:00' to '2013-10-28 14:00' - User is available for 2 hours and 
available start time is '2013-10-27 10:00' and '2013-10-27 12:00' respectively.

3 个答案:

答案 0 :(得分:1)

你去吧

SELECT t1.userId,
t1.enddate, MIN(t2.startdate),
MIN(TIMESTAMPDIFF(HOUR, t1.enddate, t2.startdate))
FROM user t1
JOIN user t2 ON t1.UserId=t2.UserId 
AND t2.startdate > t1.enddate AND t2.pid > t1.pid
WHERE
t1.endDate >= '2013-10-18 09:00'
AND t2.startDate <= '2013-11-18 19:00'
GROUP BY t1.UserId, t1.endDate

http://sqlfiddle.com/#!2/50d693/1

答案 1 :(得分:0)

使用您的数据,最简单的方法是列出某人空闲时的小时数。以下是有人可用的小时列表:

select (StartTime + interval n.n hour) as FreeHour
from (select cast('2014-05-18 11:00' as datetime) as StartTime,
             cast('2014-05-18 19:00' as datetime) as EndTime
     ) var join
     (select 0 as n union all select 1 union all select 2 union all select 3 union all select 4 union all
      select 5 union all select 6 union all select 7 union all select 8 union all select 9
     ) n
     on StartTime + interval n.n hour <= EndTime 
where not exists (select 1
                  from availability a
                  where StartTime + interval n.n hour < a.EndDate and
                        StartTime + interval n.n hour >= a.StartDate
                 );

编辑:

您的问题的一般解决方案需要对数据进行非规范化。基本查询是:

  select thedate, sum(isstart) as isstart, @cum := @cum + sum(isstart) as cum
  from ((select StartDate as thedate, 1 as isstart
         from availability a
         where userid = 42
        ) union all
        (select EndDate as thedate, -1 as isstart
         from availability a
         where userid = 42
        ) union all
        (select cast('2014-05-18 11:00' as datetime), 0 as isstart
        ) union all
        (select cast('2014-05-18 19:00' as datetime), 0 as isstart
        )
       ) t
  group by thedate cross join
           (select @cum := 0) var
  order by thedate

然后,您只需选择cum = 0的值。挑战是从这个列表中获取下一个日期。在MySQL中这是一个真正的痛苦,因为你不能使用CTE或视图或窗口函数,所以你必须重复查询。这就是为什么我认为第一种方法可能对你的情况更好。

答案 2 :(得分:0)

核心查询可以是这个。你可以随意打扮,但我会在表现层中处理所有这些......

SELECT a.enddate 'Available From'
     , MIN(b.startdate) 'To'
  FROM user a 
  JOIN user b 
    ON b.userid = a.user 
   AND b.startdate > a.enddate 
 GROUP 
    BY a.enddate
HAVING a.enddate < MIN(b.startdate)

对于“预订”范围之外的时间,您必须使用UNION扩展一点,或者再次处理应用程序级别的逻辑