conflitos de agendamento MySQL
Pergunta
Ei, eu tropecei em cima deste site à procura de soluções para as sobreposições de eventos em tabelas MySQL. Fiquei tão impressionado com a solução (que já está a ajudar) eu pensei que iria ver se eu poderia obter mais alguma ajuda ...
Ok, então Joe quer é turnos de swap com alguém no trabalho. Ele tem uma data de corte. Ele vai para a forma mudança de swap e puxar para cima agenda desta semana (ou o que sobrou dele). Isso é feito com uma consulta DB. Sem suor. Ele pega uma mudança. A partir deste ponto, torna-se espinhosa.
Assim, em primeiro lugar, a forma passa o início turno e fim do turno para o script. Ele executa uma consulta para quem tem uma mudança que se sobrepõe essa mudança. Eles não podem trabalhar dois turnos de uma vez, para que todos os IDs de usuário a partir desta consulta são colocados numa lista negra. Essa consulta é semelhante a:
SELECT DISTINCT user_id FROM shifts
WHERE
FROM_UNIXTIME('$swap_shift_start') < shiftend
AND FROM_UNIXTIME('$swap_shift_end') > shiftstart
Em seguida, executar uma consulta para todas as mudanças que são a) o mesmo comprimento (política da empresa), e b) não se sobrepõem a quaisquer outras mudanças Joe está trabalhando.
O que eu tenho atualmente é algo como isto:
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
Agora, você provavelmente está se perguntando "o que é de R $ conflict_dates ???"
Bem, quando Joe submete a mudança swap, ele recarrega suas mudanças para a semana no caso de ele decide verificar o potencial do outro turno. Assim, quando se faz essa primeira consulta, enquanto o script é um loop através de e produzir suas escolhas, ele também está construindo uma cadeia que parece tipo de:
AND NOT(
'joe_shift1_start' < shiftend
AND 'joe_shift1_end' > shiftstart)
AND NOT(
'joe_shift2_start' < shiftend
AND 'joe_shift2_end' > shiftstart)
...etc
Assim que o banco de dados está recebendo uma consulta bastante longa ao longo das linhas de:
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
Assim, a minha esperança é que o SQL tem algum caminho gênio de lidar com isso de uma maneira mais simples, ou que alguém pode apontar a principal lógica fantástica que contas para os potenciais conflitos de forma mais inteligente muito. (Observe o uso da 'começar> end, end Obrigado! A
Solução
Eu acho que você deve ser capaz de excluir outras mudanças Joe usando um interior escolha em vez da cadeia gerada, algo como:
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
Basicamente, cada linha tem uma consulta interna para a contagem de turnos de Joe que se sobrepõem, e certifica-se de que é zero. Assim, apenas as linhas que não se sobrepõem com qualquer um dos turnos existentes Joe será devolvido.
Outras dicas
Você poderia carregar os valores joe_shift{1,2,3}
em uma tabela temporária e, em seguida, fazer uma consulta para se juntar contra ela, usando uma junção externa para encontrar apenas uma mudança que não correspondem a qualquer:
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');
Por causa da LEFT OUTER JOIN, quando não há nenhuma linha correspondente na joes_shifts
, as colunas são NULL.