Pergunta

Estamos construindo um aplicativo que armazena "horas de funcionamento" para várias empresas. Qual é a maneira mais fácil para representar esses dados para que você possa facilmente verificar se um item está aberto?

Algumas opções:

  • Segmento fora blocos (cada 15 minutos) que você pode marcar "/ open fechado". Verificando envolve ver se o bit "aberto" é definido para o tempo desejado (um pouco como um cronograma de trem).
  • Armazenar uma lista de intervalos de tempo (onze horas - duas horas, 5-7pm, etc.) e verificar se a hora atual cai em qualquer intervalo especificado (isto é o que o nosso cérebro faz ao analisar as cordas acima).

Alguém tem experiência em armazenamento e consulta de informações calendário e algum conselho para dar?

(Há todos os tipos de casos de canto loucas como "fechou a primeira terça-feira do mês", mas vamos deixar isso para outro dia).

Foi útil?

Solução

armazenar cada bloco contíguo de tempo como uma hora de início e a duração; isso torna mais fácil para verificar quando as horas atravessam as fronteiras da data

Se você é certo que o horário de funcionamento nunca cruzar os limites de data (ou seja, nunca haverá uma venda-a noite toda aberto ou 72 horas maratona et al), em seguida, começar / fim dos tempos será suficiente

Outras dicas

A solução mais flexível pode ser usado a abordagem bitset. Existem 168 horas em uma semana, de forma que há 672 períodos de 15 minutos. Isso é apenas 84 bytes pena de espaço, que deve ser tolerável.

Eu usaria uma tabela como esta:

BusinessID | weekDay | OpenTime | CloseTime 
---------------------------------------------
     1          1        9           13
     1          2        5           18
     1          3        5           18
     1          4        5           18
     1          5        5           18
     1          6        5           18
     1          7        5           18

Aqui, temos uma empresa que tem um horário regular de 5 a 6, mas menos horas no domingo.

A consulta para se aberto seria (pseudo-SQL)

SELECT @isOpen = CAST
   (SELECT 1 FROM tblHours 
       WHERE BusinessId = @id AND weekDay = @Day 
       AND CONVERT(Currentime to 24 hour) IS BETWEEN(OpenTime,CloseTime)) AS BIT;

Se você precisa casos loja de ponta, então só tem 365 entradas, uma por dia ... sua realmente não muito no grande esquema das coisas, colocar um índice na coluna dia e coluna BusinessID.

Não se esqueça de armazenar o fuso horário negócios em uma tabela separada (normalize!), E realizar uma transformação entre o seu tempo e que, antes de fazer essas comparações.

Eu acho que eu, pessoalmente, ir para um tempo final de partida +, uma vez que faria tudo o mais flexível. Uma boa pergunta seria: qual é a chance de que o tamanho do bloco iria mudar em um determinado ponto? Em seguida, escolher a solução que melhor se adapta à sua situação (se é susceptível de mudar eu iria para os intervalos de tempo definately).

Você pode armazená-los como um período de tempo, e usar segmentos em sua aplicação. Dessa forma você terá a entrada fácil usando blocos, mantendo a flexibilidade para mudança em seu armazenamento de dados.

Para adicionar ao que Johnathan Holanda disse , eu permitiria múltiplas entradas para o mesmo dia.

Eu também permitiria tempo decimal, ou de outra coluna para minutos.

Por quê? muitos restaurantes e algumas empresas, e muitas empresas em todo o mundo têm almoço e ou tarde breaks. Além disso, muitos restaurantes (2 que eu saiba perto da minha casa perto pelo estranho não-incrementos de 15 tempo. Uma encerra às 21:40 aos domingos, e um encerra às 01h40.

Há também a questão de horas de férias, tais como lojas fecham mais cedo no dia de Ação de Graças, por exemplo, então você precisa ter override baseado no calendário.

Talvez o que pode ser feito é uma data / hora aberta, de data e hora perto, como este:

businessID  | datetime              | type
==========================================
        1     10/1/2008 10:30:00 AM    1
        1     10/1/2008 02:45:00 PM    0
        1     10/1/2008 05:15:00 PM    1
        1     10/2/2008 02:00:00 AM    0
        1     10/2/2008 10:30:00 AM    1

etc. (Tipo: 1 sendo aberta e fechada 0)

E se todos os dias nos próximos 1 ou dois anos precalculated 1-2 anos de antecedência. Note que você só teria 3 colunas:. Int, data / hora / bit de modo que o consumo de dados deve ser mínima

Isso também irá permitir que você modifique datas específicas para horários estranhos para dias especiais, como eles se tornam conhecidos.

Ele também cuida de cruzamento sobre a meia-noite, bem como conversões de 12/24 horas.

Também é agnóstico fuso horário. Se você armazenar o tempo de início e duração, quando você calcular o tempo do fim, é a sua máquina vai dar-lhe o tempo TZ ajustado? É isso que você quer? Mais código.

tanto quanto consulta de status de open-fechado: consulta o tempo de data em questão,

select top 1 type from thehours where datetimefield<=somedatetime and businessID = somebusinessid order by datetime desc

então olhar para "tipo". se um, é aberta, se 0, ele está fechado.

PS: Eu estava no varejo por 10 anos. Então, eu estou familiarizado com as pequenas empresas loucas-hora problemas.

OK, eu vou atirar em sobre isso por que vale a pena.

Eu preciso lidar com algumas coisas.

  • Rápido / Performant consulta
  • Qualquer incrementos de tempo, 21:01, 12:14, etc.
  • International (?) - não sei se isso é um problema mesmo com fusos horários, pelo menos no meu caso, mas alguém mais versado aqui se sentir livre para badalar na
  • Abrir - Fechar spanning para o dia seguinte (aberto ao meio-dia, perto às 2:00 AM)
  • Vários intervalos de tempo / dia
  • Capacidade de dias substituição específicos (férias, qualquer que seja)
  • Capacidade para substituições para ser recorrente
  • Capacidade de consulta para qualquer ponto no tempo e obter negócios abrir (agora, o tempo futuro, o tempo passado)
  • Capacidade de facilmente excluir resultados de empresas fechando em breve (empresas de filtro fechando em 30 minutos, você não quer fazer seus usuários 'aquele cara que aparece 5 minutos antes de fechar na indústria de alimentos / bebidas)

Eu gosto de um monte das abordagens apresentadas e eu estou pedindo a partir de alguns deles. No meu site, projeto, o que eu preciso levar em consideração que podem ter milhões de empresas e algumas das abordagens aqui parecem não escala bem para mim pessoalmente.

Aqui está o que eu proponho para um algoritmo e estrutura.

Temos que fazer algumas suposições concretas, em todo o mundo, em qualquer lugar, a qualquer hora: Há 7 dias em uma semana. Há 1440 minutos em um dia. Há um número finito de permutações de minutos de / open fechados que são possíveis.

Não concreto, mas suposições decentes: Muitas permutações de minutos aberto / fechado será compartilhada entre as empresas, reduzindo o total permutações realmente armazenados. Houve um tempo em minha vida que eu poderia facilmente calcular as combinações reais possíveis a esta abordagem, mas se alguém poderia ajudar / acha que seria útil, que seria ótimo.

proponho 3 tabelas: Antes de parar de ler, considerar no mundo real 2 destes quadros será de cache bastante pequena ordenadamente. Esta abordagem não vai ser para todos, quer devido à enorme complexidade do código necessário para interpretar uma interface de usuário para o modelo de dados e de volta, se necessário. Sua milhagem e necessidades podem variar. Esta é uma tentativa de uma solução nível razoável 'empresa', o que isso significa.

HoursOfOperations Tabela

ID | ABERTO (hora do dia) | CLOSE (hora do dia)


1 | 360 | 1020 (exemplo: 09:00-17:00)

2 | 365 | 1021 (exemplo: borda caso 9h05 - 17:01 (esquisitos))

etc.

HoursOfOperations não se preocupa com o que dias, basta abrir e fechar e singularidade. Pode haver apenas uma única entrada, por combinação de abrir / fechar. Agora, dependendo do seu ambiente, quer esta tabela inteira pode ser armazenada em cache ou pode ser armazenada em cache para a hora atual do dia, etc. De qualquer forma, você não precisa consultar esta tabela para cada operação. Dependendo da sua solução de armazenamento eu imagino cada coluna nesta tabela como indexados para o desempenho. Enquanto o tempo avança, esta tabela de probabilidade tem uma probabilidade exponencialmente inversa do implante (s). Realmente, porém, lidar com esta tabela deve ser maioritariamente uma operação em processo (RAM).

Business2HoursMap

Nota: No meu exemplo, eu estou armazenando "Dia" como um campo de bit-bandeira / coluna. Isto é principalmente devido às minhas necessidades e o avanço da LINQ / Bandeiras Enums em C #. Não há nada que impeça você de expandir isso para 7 campos de bits. Ambas as abordagens devem ser relativamente semelhante em ambos abordagem lógica de armazenamento e consulta.

Outra Nota:. Eu não estou entrando em uma discussão semântica em "cada mesa tem uma coluna PK ID", por favor encontrar um outro fórum para que

BusinessID | HoursID | Dia (ou, se preferir dividir em: BIT segunda-feira, BIT terça-feira, ...)


1 | 1 | 1111111 (este negócio está aberto 9-5 todos os dias da semana)

2 | 2 | 1111110 (este negócio está aberto 09:05 - 05:01 M-Sat (de segunda = dia 1)

A razão isto é fácil de consulta é que sempre podemos determinar facilmente o MOTD (minuto do dia) que nós estamos procurando. Se eu quero saber o que está aberto às 5 da tarde amanhã eu pegar todos HoursOfOperations IDS ONDE Fechar> = 1020. A menos que eu estou procurando um intervalo de tempo, Open torna-se insignificante. Se você não quer mostrar empresas fechando na próxima meia hora, basta ajustar o seu tempo de entrada em conformidade (procurar 5:30 PM (1050), não 05:00 (1020). A segunda consulta seria naturalmente 'dá-me todos os negócios com HoursID IN (1, 2, 3, 4, 5), etc. Isso provavelmente deve levantar uma bandeira vermelha, pois há limitações para esta abordagem. No entanto, se alguém pode responder à pergunta permutações reais acima, pode ser capaz de puxar a bandeira para baixo vermelho. Considere precisamos apenas as permutações possíveis em qualquer um dos lados da equação de uma só vez, quer abrir ou fechar.

Considerando que temos nossa primeira cache de mesa, que é uma operação rápida. Segunda operação está consultando esta tabela potencialmente grande fila, mas estamos procurando muito pequenas (SMALLINT) colunas espero indexados.

Agora, você pode estar vendo a complexidade do lado do código de coisas. Eu estou visando principalmente bares no meu projeto particular, por isso vai ser muito seguro assumir que vou ter um número considerável de empresas com horários como "11:00 - 02h00 (no dia seguinte)". Isso com certeza seria 2 entradas para a tabela de HoursOfOperations, bem como a tabela de Business2HoursMap. Por exemplo. uma barra que é aberta a partir de 11:00-2h00 terá 2 referências a tabela de HoursOfOperations 660 - 1440 (11:00 - meia-noite) e 0 - 120 (meia-noite - 02:00 AM). Essas referências seriam refletidos nos dias atuais na tabela Business2HoursMap como 2 entradas no nosso caso simplista, 1 entrada = todos os dias Horas Referência # 1, mais todos os dias Referência # 2. Esperança de que faz sentido, tem sido um longo dia.

Substituindo em dias especiais / feriados / whatever. Substituições são, por natureza, data base, não o dia da semana com base. Acho que este é o lugar onde algumas das abordagens tentam empurrar o pino redondo proverbial em um buraco quadrado. Precisamos de outra tabela.

HoursID | BusinessID | dia | mês | Ano

1 | 2 | 1 | 1 | NULL

Isso certamente pode ficar mais complexo se você precisava de algo como "em cada segunda terça-feira, a empresa vai pescar durante 4 horas". No entanto, o que isso vai permitir-nos fazer muito facilmente é permitir 1 - substituições, 2 - substituições recorrentes razoáveis. POR EXEMPLO. se ano é NULL, em seguida, todos os anos, no dia de Ano Novo este bar esquisitão está aberto 09h00 - 17:00 tendo em linha com os nossos exemplos dados acima. Ou seja, - Se no ano foram definidas, é apenas para 2013. Se mês é nula, é cada primeiro dia do mês. Novamente, isto não irá lidar com todos os cenários de agendamento por colunas NULL sozinho, mas, teoricamente, você poderia lidar com praticamente qualquer coisa por depender de uma longa sequência de datas absolutas, se necessário.

Mais uma vez, gostaria de armazenar em cache esta tabela no dia de rolamento. Eu simplesmente não pode realisticamente ver as linhas para esta tabela em um único dia snapshot sendo muito grande, pelo menos para as minhas necessidades. Gostaria de verificar esta tabela pela primeira vez como é bom, uma substituição e iria salvar uma consulta em relação a tabela Business2HoursMap muito maior no lado do armazenamento.

problema interessante. Estou realmente surpreendido esta é a primeira vez que eu realmente precisava pensar nisso. Como sempre, muito interessada em diferentes perspectivas, abordagens ou falhas na minha abordagem.

Os blocos segmento são melhores, apenas certifique-se de dar ao usuário uma maneira fácil de definir. Clique e arraste é bom.

Qualquer outro sistema (como faixas) vai ser realmente irritante quando você cruza a fronteira da meia-noite.

Quanto à forma como você armazená-los, em bitfields C ++ provavelmente seria melhor. Na maioria dos outros idiomas, e variedade pode ser melhor (lotes de espaço desperdiçado, mas iria correr mais rápido e mais fácil de compreender).

Gostaria de pensar um pouco sobre essas borda casos agora, porque eles estão indo para informar se você tem uma configuração de base acrescido de sobreposição ou de armazenagem estática completa de horários de abertura ou o que quer.

Há tantas exceções - e em uma base regular (como dias de neve, feriados irregulares como Páscoa, Sexta-feira Santa), que, se isso é esperado para ser uma representação confiável da realidade (em oposição a um bom palpite), você 'necessidade ll para enfrentá-lo muito em breve na arquitetura.

Como sobre algo como isto:

Loja Horas Tabela

Business_id (int)
Start_Time (time)
End_Time (time)
Condition varchar/string
Open bit

'Estado' é uma expressão lambda (texto de uma cláusula 'onde'). Construir a consulta dinamicamente. Assim, para um negócio particular que você selecione todas as vezes abrir / fechar

Let Query1 = select count(open) from store_hours where @t between start_time and end_time and open  = true and business_id = @id and (.. dynamically built expression)

Let Query2 = select count(closed) from store_hours where @t between start_time and end_time and open = false and business_id = @id and (.. dynamically built expression)

Assim termina no final você quer algo como:

select cast(Query1 as bit) & ~cast(Query2 as bit)

Se o resultado da última consulta é 1, então a loja é aberta no tempo t, caso contrário, ele é fechado.

Agora você só precisa de uma interface amigável que pode gerar o seu onde cláusulas (expressões lambda) para você.

O único outro caso de canto que eu posso pensar é o que acontece se uma loja está aberta das digamos 07:00-2:00 em uma data, mas fecha às 23:00 na data seguinte. O sistema deve ser capaz de lidar com isso bem, dividindo de forma inteligente até os tempos entre os dois dias.

Há certamente não precisa de memória conserva aqui, mas talvez a necessidade de um código limpo e compreensível. "Bit twiddling" não é, IMHO, o caminho a percorrer.

Precisamos de um recipiente set aqui, que detém qualquer número de itens exclusivos e pode determinar rápida e facilmente se um item é um membro ou não. Os reuires configuração importa, mas em uso rotineiro de uma única linha de código entendida simplesmente determina se você está aberto ou fechado

Conceito: Atribuir número de índice para cada bloco de 15 minutos, a partir de, digamos, domingo à meia-noite.

Inicializar: Inserir um conjunto o número de índice de cada bloco 15 min quando estiver aberta. (Supondo que você está aberto menos horas do que você estão fechados.)

Use: Subtrair momento interessante, em minutos, a meia-noite do domingo anterior e dividir por 15. Se este número esteja presente no conjunto, você está aberto.

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