Estou projetando uma tabela postgres para registrar os intervalos contidos em uma programação semanal. Ele manteria agendas para várias empresas, e um conjunto de dados de exemplo simples pode ser semelhante a:
business_id interval
----------- -----------------------------------
1 Sunday 10:00:00 – Sunday 14:00:00
1 Sunday 22:00:00 – Monday 02:00:00
1 Friday 11:00:00 – Friday 16:00:00
1 Saturday 15:00:00 – Sunday 01:00:00
Observe que os intervalos podem cruzar os limites entre os dias.
Uma empresa não deve ter intervalos sobrepostos e gostaria de projetar a tabela de uma forma que me permitisse impor isso.
Eu estava pensando em mapear esses valores de dia da semana + hora do dia para os números correspondentes de segundos desde o início da semana, armazenando intervalos como int4range
e usando uma restrição de exclusão para proibir a sobreposição de intervalos inteiros, mas isso não seria adequado intervalos de endereços que envolvem o final da semana.
Existe uma boa maneira de modelar esse tipo de dados cíclicos e proibir sobreposições?
Mapeie cada valor de dia da semana + hora do dia para o número correspondente de segundos desde o início da semana em que o intervalo começou, armazene os intervalos em uma
int4range
coluna e adicione duas restrições de exclusão.A primeira restrição de exclusão proíbe intervalos que se sobrepõem sem envolver o final da semana:
A segunda restrição de exclusão proíbe intervalos que se sobrepõem devido a um deles envolver o final da semana:
As restrições de exclusão são a parte interessante, mas, para completar, a tabela em si seria algo como:
Este é um caso de uso perfeito para o
RANGE
tipo (fantástico) do PostgreSQL. Todo o código abaixo está disponível no violino aqui .Um excelente artigo sobre eles pode ser encontrado aqui (por Dimitri Fontaine) - o principal a observar é:
O principal a observar é que um intervalo é ordenado - o primeiro valor é menor que o segundo, portanto, não há medo de compromissos sem sentido que começam após o término (e já vi sistemas que realmente permitem isso!).
Um post interessante sobre como eles podem ser usados para "transformar 100 linhas de SQL em 3" pode ser encontrado aqui ( tipos multi-range nb ).
Tabela de amostra simples:
Então, isso nos diz que nenhum
intval
pode se sobrepor a outro com o mesmobusiness_id
(pessoa, recurso...).Testar:
e depois:
Sem problemas!
Mas!
e obtemos (corretamente):
e agora com um diferente
business_id
, mas se sobrepõe aos horários anteriores:Resultado (como esperado):
Não há necessidade de se preocupar com domingos, segundas-feiras ou intervalos que ultrapassam os limites do dia, semana, mês ou ano. Esses tipos de intervalo são incrivelmente poderosos e valem o esforço de conhecê-los. Veja aqui uma lista de funções e operadores.
Seguindo os comentários do OP, aqui está um código para configurar algum tipo de agendamento de turno. Provavelmente poderia fazer algum tratamento com PL/pgSQL para arrumar, para agendamentos recorrentes, como linha de base, pode ajudar.
Seu comentário diz (presumivelmente como um exemplo)
"Mondays 9AM to 5PM, Tuesdays 10AM to 6PM, ..
, então eu atendi a esse exemplo - obviamente você pode adicionar seu próprio código para cenários mais complexos. Você também pode usar o tipo de dados range conforme descrito acima.Resultado (recortado por brevidade):
Então, você pode ver aqui (e se você examinar o violino) que as segundas-feiras das 09:00 às 17:00 são horas de turno e as terças das 10:00 às 18:00 são horas de turno.