Найти перекрывающиеся (дата / время) строки в одной таблице - PullRequest
14 голосов
/ 27 марта 2009

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

meetingID int
meetingStart datetime
ВстречаEnd datetime

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

Какой самый эффективный способ выполнить такой запрос в MySQL?

Ответы [ 4 ]

23 голосов
/ 27 марта 2009
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

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

1 голос
/ 16 октября 2015

Попробуйте использовать этот запрос. Это решение 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
0 голосов
/ 27 марта 2009

Добавление времени начала и окончания обоих собраний в строки результатов:

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

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

0 голосов
/ 27 марта 2009

Возможно что-то вроде этого:

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)

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...