Конфликты расписания MySQL
Вопрос
Привет, я наткнулся на этот сайт в поисках решений для перекрытия событий в таблицах MySQL.Я был ТАК впечатлен решением (которое уже помогает) Я подумал, что посмотрю, смогу ли я получить еще какую-нибудь помощь...
Ладно, значит, Джо хочет поменяться сменами с кем-нибудь на работе.У него назначена дата суда.Он переходит к форме обмена сменами, и она выводит расписание на эту неделю (или то, что от него осталось).Это делается с помощью запроса к базе данных.Не парься.Он выбирает смену.С этого момента она становится колючей.
Итак, во-первых, форма передает скрипту начало сдвига и конец сдвига.Он запускает запрос для всех, у кого есть сдвиг, который перекрывает этот сдвиг.Они не могут работать в две смены одновременно, поэтому все идентификаторы пользователей из этого запроса заносятся в черный список.Этот запрос выглядит следующим образом:
SELECT DISTINCT user_id FROM shifts
WHERE
FROM_UNIXTIME('$swap_shift_start') < shiftend
AND FROM_UNIXTIME('$swap_shift_end') > shiftstart
Затем мы запускаем запрос для всех смен, которые а) имеют одинаковую продолжительность (политика компании) и б) не пересекаются ни с какими другими сменами, на которых работает Джо.
То, что у меня сейчас есть, это что-то вроде этого:
SELECT *
FROM shifts
AND shiftstart BETWEEN FROM_UNIXTIME('$startday') AND FROM_UNIXTIME('$endday')
AND user_id NOT IN ($busy_users)
AND (TIME_TO_SEC(TIMEDIFF(shiftend,shiftstart)) = '$swap_shift_length')
$conflict_dates
ORDER BY shiftstart, lastname
Теперь вы, вероятно, задаетесь вопросом "что такое $conflict_dates???"
Что ж, когда Джо отправляет сменную смену, это перезагружает его смены на неделю на случай, если он решит проверить потенциал другой смены.Поэтому, когда он выполняет этот первый запрос, в то время как скрипт перебирает и выводит свои варианты, он также создает строку, которая выглядит примерно так:
AND NOT(
'joe_shift1_start' < shiftend
AND 'joe_shift1_end' > shiftstart)
AND NOT(
'joe_shift2_start' < shiftend
AND 'joe_shift2_end' > shiftstart)
...etc
Таким образом, база данных получает довольно длинный запрос в соответствии с:
SELECT *
FROM shifts
AND shiftstart BETWEEN FROM_UNIXTIME('$startday') AND FROM_UNIXTIME('$endday')
AND user_id NOT IN ('blacklisteduser1', 'blacklisteduser2',...etc)
AND (TIME_TO_SEC(TIMEDIFF(shiftend,shiftstart)) = '$swap_shift_length')
AND NOT(
'joe_shift1_start' < shiftend
AND 'joe_shift1_end' > shiftstart)
AND NOT(
'joe_shift2_start' < shiftend
AND 'joe_shift2_end' > shiftstart)
AND NOT(
'joe_shift3_start' < shiftend
AND 'joe_shift3_end' > shiftstart)
AND NOT(
'joe_shift4_start' < shiftend
AND 'joe_shift4_end' > shiftstart)
...etc
ORDER BY shiftstart, lastname
Итак, я надеюсь, что либо у SQL есть какой-то гениальный способ справиться с этим более простым способом, либо кто-то может указать на фантастический логический принцип, который объясняет потенциальные конфликты гораздо более разумным способом.(Обратите внимание на использование 'начало > конец, end < начать", прежде чем я обнаружил, что использую промежуточные значения и мне пришлось вычесть по минуте с обоих концов.)
Спасибо!
A
Решение
Я думаю, вы должны иметь возможность исключить другие сдвиги Джо, используя внутренний выбор вместо сгенерированной строки, что-то вроде:
SELECT *
FROM shifts s1
AND shiftstart BETWEEN FROM_UNIXTIME('$startday') AND FROM_UNIXTIME('$endday')
AND user_id NOT IN ($busy_users)
AND (TIME_TO_SEC(TIMEDIFF(shiftend,shiftstart)) = '$swap_shift_length')
AND (SELECT COUNT(1) FROM shifts s2
WHERE s2.user_id = $joes_user_id
AND s1.shiftstart < s2.shiftend
AND s2.shiftstart < s1.shiftend) = 0
ORDER BY shiftstart, lastname
По сути, каждая строка имеет внутренний запрос на количество смен Джо, которые перекрываются, и проверяет, что оно равно нулю.Таким образом, будут возвращены только строки, которые не перекрываются ни с одной из существующих смен Джо.
Другие советы
Вы могли бы загрузить joe_shift{1,2,3}
значения во ВРЕМЕННУЮ таблицу, а затем выполните запрос для объединения с ней, используя внешнее объединение, чтобы найти только те сдвиги, которые не соответствуют ни одному:
CREATE TEMPORARY TABLE joes_shifts (
shiftstart DATETIME
shiftend DATETIME
);
INSERT INTO joes_shifts (shiftstart, shiftend) VALUES
('$joe_shift1_start', '$joe_shift1_end'),
('$joe_shift2_start', '$joe_shift2_end'),
('$joe_shift3_start', '$joe_shift3_end'),
('$joe_shift4_start', '$joe_shift4_end');
-- make sure you have validated these variables to prevent SQL injection
SELECT s.*
FROM shifts s
LEFT OUTER JOIN joes_shifts j
ON (j.shiftstart < s.shiftend OR j.shiftend > s.shiftstart)
WHERE j.shiftstart IS NULL
AND s.shiftstart BETWEEN FROM_UNIXTIME('$startday') AND FROM_UNIXTIME('$endday')
AND s.user_id NOT IN ('blacklisteduser1', 'blacklisteduser2',...etc)
AND (TIME_TO_SEC(TIMEDIFF(s.shiftend,s.shiftstart)) = '$swap_shift_length');
Из-за ЛЕВОГО ВНЕШНЕГО СОЕДИНЕНИЯ, когда нет совпадающей строки в joes_shifts
, столбцы равны НУЛЮ.