【问题标题】:Find overlapping (date/time) rows within one table在一个表中查找重叠(日期/时间)行
【发布时间】:2010-10-15 22:10:44
【问题描述】:

我有一个表格,它在每一行中存储一个会议,其中包含开始日期/时间和结束日期/时间。

meetingID int
会议开始日期时间
会议结束日期时间

期望的输出: 对于每对重叠的行,我想输出
会议ID,会议开始,会议ID,会议结束

在 MySQL 中执行此类查询最有效的方法是什么?

【问题讨论】:

标签: mysql


【解决方案1】:
SELECT  m1.meetingID, m1.meetingStart, m1.meetingEnd, m2.meetingID
FROM    t_meeting m1, t_meeting m2
WHERE   (m2.meetingStart BETWEEN m1.meetingStart AND m1.meetingEnd
        OR m2.meetingEnd BETWEEN m1.meetingStart AND m1.meetingEnd)
        AND m1.meetingID <> m2.meetingID

这将选择每对两次。

如果您希望每对只选择一次,请使用:

SELECT  m1.meetingID, m1.meetingStart, m1.meetingEnd, m2.meetingID
FROM    t_meeting m1, t_meeting m2
WHERE   (m2.meetingStart BETWEEN m1.meetingStart AND m1.meetingEnd
        OR m2.meetingEnd BETWEEN m1.meetingStart AND m1.meetingEnd)
        AND m2.meetingID > m1.meetingID

确保您在 meetingStartmeetingEnd 上有索引,以便查询有效地工作。

然而,MySQL 可能会使用INDEX MERGE 来运行此查询,这在当前实现中效率不高。

您也可以尝试使用:

SELECT  m1.*, m2.*
FROM    (
        SELECT  m1.meetingID AS mid1, m2.meetingID AS mid2
        FROM    t_meeting m1, t_meeting m2
        WHERE   m2.meetingStart BETWEEN m1.meetingStart AND m1.meetingEnd
                AND m2.meetingID <> m1.meetingID
        UNION
        SELECT  m1.meetingID, m2.meetingID
        FROM    t_meeting m1, t_meeting m2
        WHERE   m2.meetingEnd BETWEEN m1.meetingStart AND m1.meetingEnd
                AND m2.meetingID <> m1.meetingID
        ) mo, t_meeting m1, t_meeting m2
WHERE   m1.meetingID = mid1
        AND m2.meetingID = mid2

,它更复杂,但很可能会运行得更快一些。

【讨论】:

  • 在最后一个查询中,可能添加“AND m2.meetingID > m1.meetingID”以防止双打。
  • 非常感谢!这很好用!我真的很感谢你的帮助。问候,马丁
  • 这可行,但在会议环境中,您可能会得到不需要的结果。例如,如果您在下午 5 点到 6 点和下午 6 点到 7 点有会议,则匹配,因为它们都有下午 6 点
  • @Señor Reginold Francis 以及如何让他们在下午 6 点不匹配?我们需要加上 +- 1 分钟,对吧?
【解决方案2】:

尝试使用此查询。 Quassnoi 的解决方案被修改为忽略一个预订的结束与另一个预订的开始相同的情况。

SELECT  m1.meetingID_id, m1.meetingStart , m1.meetingEnd, m2.meetingID_id
FROM    bookings m1, bookings m2
WHERE   (m2.meetingStart BETWEEN m1.start AND DATE_SUB(m1.meetingEnd, INTERVAL 1 second)
        OR DATE_SUB(m2.meetingEnd, INTERVAL 1 second) BETWEEN m1.meetingStart AND m1.end)
        AND m1.meetingID_id > m2.meetingID_id

【讨论】:

    【解决方案3】:

    大概是这样的:

    SELECT m1.meetingID, m2.meetingID
    FROM meeting AS m1, meeting AS m2
    WHERE m1.meetingID < m2.meetingID
        AND m1.meetingStart BETWEEN m2.meetingStart AND m2.meetingEnd
        OR m1.meetingEnd BETWEEN m2.meetingStart AND m2.meetingEnd
    

    通过仅选择 m1.meetingID &lt; m2.meetingID,您不会将行与其自身进行比较,也不会出现重复,因为每行将连接两次 (m1, m2) 和 (m2, m1)

    【讨论】:

      【解决方案4】:

      将会议的开始和结束时间添加到结果行:

      SELECT m1.meetingID AS firstID, m1.meetingStart AS firstStart, 
      m1.meetingEnd AS firstEnd, m2.meetingID AS secondID, 
      m2.meetingStart AS secondStart, m2.meetingEnd AS secondEnd 
      FROM meeting AS m1, meeting AS m2 
      WHERE (m2.meetingStart BETWEEN m1.meetingStart AND m1.meetingEnd) 
      AND (m1.meetingID != m2.meetingID)
      

      这样 m2 将始终是在同一时间或在 m1 之后开始的那个, 并且 m1.id!=m2.id 确保它不会包含针对自身的匹配项。

      您不需要检查会议结束,因为重叠 仅通过比较会议开始就可以可靠地检测到。

      【讨论】:

        猜你喜欢
        • 2018-08-29
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多