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

Foi útil?

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.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top