Найти строки перекрытия (дата/время) в одной таблице
У меня есть таблица, в которой хранятся в каждой строке встреча с датой начала/временем и датой окончания/временем.
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 гарантирует, что он не будет содержать совпадений с самим собой.
Вам не нужно проверять на конец собрания, так как перекрытие
могут быть надежно обнаружены из простого сравнения начала собрания.