查找重叠日期

时间:2010-08-03 04:48:06

标签: sql mysql

我有一套会议室和会议,其中包括开始日期和结束日期。一套会议室属于建筑物。

会议详细信息保存在具有startDate和endDate的MeetingDetail表中。 现在我要在两个时间段之间发出报告,例如reportStartDate和reportEndDate,它会找到为给定建筑物预订所有会议室的时间段

表结构 MEETING_ROOM - ID,ROOMNAME,BUILDING_NO

MEETING_DETAIL - ID,MEETING_ROOM_ID,START_DATE,END_DATE

必须为reportStartDate和REportEndDate

触发查询

为了进一步澄清,目的是找到报告的每个时间段内报告的所有时间段:reportStartDate和reportEndDate

2 个答案:

答案 0 :(得分:4)

对于SQL Server 2005+,您可以尝试以下(请参阅mysql末尾的注释)

WITH TIME_POINTS (POINT_P) AS
(SELECT DISTINCT START_DATE FROM MEETING_DETAIL 
          WHERE START_DATE > @reportStartDate AND START_DATE < @reportEndDate
     UNION SELECT DISTINCT END_DATE FROM MEETING_DETAIL 
          WHERE END_DATE > @reportStartDate AND END_DATE < @reportEndDate
     UNION SELECT @reportEndDate
     UNION SELECT @reportStartDate),

WITH TIME_SLICE (START_T, END_T) AS 
(SELECT A.POINT_P, MIN(B.POINT_P) FROM 
     TIMEPOINTS A
     INNER JOIN TIMEPOINTS B ON A.POINT_P > B.POINT_P
GROUP BY A.POINT_P),

WITH SLICE_MEETINGS (START_T, END_T, MEETING_ROOM_ID, BUILDING_NO) AS
(SELECT START_T, END_T, MEETING_ROOM_ID, BUILDING_NO FROM
     TIME_SLICE A
     INNER JOIN MEETING_DETAIL B ON B.START_DATE <= A.START_T AND B.END_DATE >= B.END_T
     INNER JOIN MEETING_ROOM C ON B.MEETING_ROOM_ID = C.ID),

WITH SLICE_COUNT (START_T, END_T, BUILDING_NO, ROOMS_C) AS
(SELECT START_T, END_T, BUILDING_NO, COUNT(MEETING_ROOM_ID) FROM
     SLICE_MEETINGS
     GROUP BY START_T, END_T, BUILDING_NO),

WITH ROOMS_BUILDING (BUILDING_NO, ROOMS_C) AS
(SELECT BUILDING_NO, COUNT(ID) FROM
     MEETING_ROOM
     GROUP BY BUILDING_NO)

SELECT B.BUILDING_NO, A.START_T, A.END_T
     FROM SLICE_COUNT A.
     INNER JOIN ROOMS_BUILDING B WHERE A.BUILDING_NO = B.BUILDING_NO AND B.ROOMS_C = A.ROOMS_C;

它做的是(每一步对应于上面的每个CTE定义)

  • 获取所有时间标记,即结束或开始时间
  • 获取所有时间片,即没有其他时间标记的最小时间单位(即没有会议在时间片中开始,它在时间片的开头或结尾)
  • 为每个时间片获取会议,现在您可以获得类似
  • 的内容
    10.30   11.00    Room1   BuildingA
    10.30   11.00    Room2   BuildingA
    11.00   12.00    Room1   BuildingA
  • 获取每个时间片每个建筑物预订的房间数
  • 过滤掉与每栋建筑物中的房间数相匹配的时间片建筑组合

修改

由于mysql不支持WITH子句,因此您必须为上面的每个(5个)WITH clases构造视图。其他一切都会保持不变。

答案 1 :(得分:2)

阅读完评论后,我想我对这个问题的理解有点好。作为第一步,我将使用cross join

生成会议室和时段矩阵
select  *
from    (
        select  distinct start_date
        ,       end_date
        from    @meeting_detail     
        ) ts
cross join  
        @meeting_room mr

然后,对于矩阵中的每个单元格,在该时间段中添加会议:

left join    
        @meeting_detail md
on      mr.id = md.meeting_room_id
        and ts.start_date < md.end_date
        and md.start_date < ts.end_date

然后要求没有免费房间。例如,通过说左连接必须对所有房间和时间段都成功。如果任何字段不为null,则左连接成功:

group by
        mr.building_no
,       ts.start_date
,       ts.end_date
having  max(case when md.meeting_room_id is null 
                 then 1 else 0 end) = 0

这是一个完整的工作示例。它是为SQL Server编写的,表变量(@meeting_detail)在MySQL中不起作用。但报告生成查询应该适用于大多数数据库:

set nocount on

declare @meeting_room table (id int, roomname varchar(50), 
    building_no int)
declare @meeting_detail table (meeting_room_id int, 
    start_date datetime, end_date datetime)

insert @meeting_room (id, roomname, building_no)
          select  1, 'Kitchen', 6
union all select  2, 'Ballroom', 6
union all select  3, 'Conservatory', 7
union all select  4, 'Dining Room', 7

insert @meeting_detail (meeting_room_id, start_date, end_date)
          select 1, '2010-08-01  9:00', '2010-08-01 10:00'
union all select 1, '2010-08-01 10:00', '2010-08-01 11:00' 
union all select 2, '2010-08-01 10:00', '2010-08-01 11:00' 
union all select 3, '2010-08-01 10:00', '2010-08-01 11:00' 


select  mr.building_no
,       ts.start_date
,       ts.end_date
from    (
        select  distinct start_date
        ,       end_date
        from    @meeting_detail     
        ) ts
cross join  
        @meeting_room mr
left join    
        @meeting_detail md
on      mr.id = md.meeting_room_id
        and ts.start_date < md.end_date
        and md.start_date < ts.end_date
group by
        mr.building_no
,       ts.start_date
,       ts.end_date
having  max(case when md.meeting_room_id is null 
                 then 1 else 0 end) = 0

打印:

building_no   start                    end
6             2010-08-01 10:00:00.000  2010-08-01 11:00:00.000