Найти строки перекрытия (дата/время) в одной таблице

У меня есть таблица, в которой хранятся в каждой строке встреча с датой начала/временем и датой окончания/временем.

meetingID int
meetingStart datetime
meetingEnd datetime

Требуемый вывод: Для каждой пары перекрывающихся строк я хотел бы выводить meetingID, meetingStart, meetingID, meetingEnd

Какой самый эффективный способ выполнить такой запрос в 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

Убедитесь, что у вас есть индексы на meetingStart и meetingEnd, чтобы запрос работал эффективно.

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

что является более сложным, но, скорее всего, будет работать немного быстрее.

Ответ 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 < 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 гарантирует, что он не будет содержать совпадений с самим собой.

Вам не нужно проверять на конец собрания, так как перекрытие могут быть надежно обнаружены из простого сравнения начала собрания.