Almacenamiento del horario comercial en una base de datos

84

Actualmente estoy tratando de encontrar la mejor manera de almacenar las horas de operación de una empresa en una base de datos.

Por ejemplo:

La empresa A tiene las siguientes horas de funcionamiento

  • Lunes: 9h - 17h
  • Martes: 9 a.m. - 5 p.m.
  • Miércoles: 9 a. M. - 5 p. M.
  • Jueves: 9am - 5pm
  • Viernes: 9h - 17h
  • Sábado: 9 a.m. - 12 del mediodía
  • Domingo: cerrado

Actualmente tengo un modelo de datos similar al siguiente

CREATE TABLE "business_hours" (
    "id" integer NOT NULL PRIMARY KEY,
    "day" varchar(16) NOT NULL,
    "open_time" time,
    "close_time" time
)

donde el "día" está restringido a una selección de los 7 días de la semana en código (a través del ORM). Para probar si una empresa está cerrada en un día determinado, verifica si open_time y close_time son NULL. Se relaciona con el negocio a través de una tabla intermedia (Relación Many To Many).

¿Alguien tiene alguna sugerencia para este esquema de base de datos? Hay algo en eso que no me parece correcto.

usuario128000
fuente
4
¿Por qué necesita una relación M-to-M entre la tabla business_hours y la tabla negocios? Si realmente va a hacer que varias empresas compartan el mismo récord en business_hours, ¿por qué? Semánticamente, el hecho de que "la empresa C trabaja de T1 a T2 el día D" es más un objeto de valor que una entidad ... La única razón buena (?) Que puedo imaginar es la optimización del tamaño del almacenamiento (versión RDB del patrón Flyweight, por así decirlo) si se espera que la cantidad de negocios sea enorme ...
Yarik
2
¿Qué pasa con las pausas para el almuerzo? ¿Y los días festivos?
Mawg dice reinstalar a Monica

Respuestas:

56

En general, no veo nada de malo en esto. Excepto...

  1. Almacenaría el día de la semana como un número entero usando cualquier sistema de numeración que use su lenguaje de programación nativo (en sus bibliotecas). Esto disminuirá el tamaño de la base de datos y eliminará las comparaciones de cadenas de su código.

  2. Probablemente pondría la clave externa en la mesa de negocios aquí mismo en esta tabla. De esa forma no necesitará una tabla de enlaces.

Así que supongo que haría:

CREATE TABLE "business_hours" (
     "id" integer NOT NULL PRIMARY KEY,
     "business_id" integer NOT NULL FOREIGN KEY REFERENCES "businesses",
     "day" integer NOT NULL,
     "open_time" time,
     "close_time" time
)

En mi lógica empresarial, impondría la restricción de que cada "negocio" tiene al menos 7 "horas laborales". ( Al menos porque Jon Skeet tiene razón, es posible que desee horas de vacaciones). Aunque es posible que desee relajar esta restricción simplemente dejando fuera el "horario comercial" para los días en que la empresa está cerrada.

Frank Krueger
fuente
1
¿Cómo harías 2? ¿Tiene todas las entradas para todas las empresas incluso cuando el día y la hora son los mismos?
Vinko Vrsalovic
7
Yo diría que sí. De lo contrario, considere lo que sucedería si dos empresas comparten las mismas horas, pero luego una de ellas necesita cambiar sus horas. Tendría que detectar el hecho de que se modifican las horas y crear un nuevo registro o editar el existente, según sea compartido o no.
Erik Forbes
3
Absolutamente no compartiría filas de esta tabla entre empresas. Ese tipo de compartir solo conduce al dolor. La lógica empresarial de la aplicación debería simplemente imponer la restricción de que cada empresa tiene al menos 7 referencias "business_hours".
Frank Krueger
1
@Vinko: Recuerde que si bien los valores son (actualmente) los mismos, los horarios de apertura de dos negocios son semánticamente distintos.
Draemon
1
También puede utilizar un mapa de bits de 7 bits para "día". De esa forma no tendrá que repetir las mismas entradas. Ejemplo, todos los días laborables tienen el mismo horario comercial, una entrada es suficiente.
ZolaKt
28

Una situación que no está cubierta por este esquema son varios períodos de apertura en un día. Por ejemplo, el pub local está abierto de 12:00 a 14:30 y de 17:00 a 23:00.

Tal vez la taquilla de un teatro esté abierta para una sesión matinal y una función nocturna.

En ese momento, debe decidir si puede tener varias entradas para el mismo día o si necesita representar diferentes horas en la misma fila.

¿Qué pasa con los horarios de apertura que cruzan la medianoche? Digamos que un bar está abierto de 19: 00-02: 00. No puede simplemente comparar los horarios de apertura y cierre con el tiempo que desea probar.

Davidsheldon
fuente
Terminé haciendo algo como sugirió @JordanFeldstein. Guardo matrices de "cambios de estado", que incluyen el cambio (abrir o cerrar), el día de la semana y la hora del día. Puedo tener tantos "cambios" como quiera para cada negocio y cada día. Una empresa puede abrir un día y cerrar el siguiente, abrir dos (o x veces) al día, estar abierta las 24 horas pero cerrar los lunes, lo que sea. Es complicado de implementar, pero muy flexible.
ironcito
Siguiendo el enfoque de la mejor respuesta, prefiero agregar dos columnas a business_hours: break_timey break_duration. Es muy raro tener 2 descansos el mismo día.
Frondor
12

Depende en cierto modo de para qué necesita almacenarlos y de cómo podrían verse los datos del mundo real.
Si necesita poder determinar si la empresa está abierta en un momento determinado, puede resultar un poco incómodo consultar el esquema tal como se presenta. Sin embargo, lo más importante es: ¿alguna vez necesitaría atender un cierre al mediodía?

Algunas opciones incluyen;

  • Un esquema como el que tienes, pero con la opción de tener múltiples periodos para el mismo día. Sería adecuado para la pausa del almuerzo, pero haría incómodo ejecutar una consulta que le proporcione el horario de apertura de un día determinado, por ejemplo, para la presentación a un usuario.
  • Un enfoque de estilo de mapa de bits; "000000000111111110000000" para 9-5. La desventaja de este enfoque es que debe elegir una granularidad específica, es decir, horas completas o medias horas o, de hecho, minutos. Cuanto más fina sea la granularidad, más difícil será la lectura de los datos para un humano. Puede usar operadores bit a bit para almacenar este valor como un solo número en lugar de una cadena de enteros, pero nuevamente perjudica la legibilidad.
Frans
fuente
11

He aprendido que si desea que el marcado de datos de Google reconozca sus datos, debe seguir estas pautas:

https://schema.org/openingHours

http://schema.org/OpeningHoursSpecification Contiene "fechas válidas", lo cual es muy útil para algunas empresas.

https://schema.org/docs/search_results.html#q=hours

Debería estar bien sin una clave principal, a menos que permita que las empresas compartan las mismas horas con la tabla de combinación; es interesante que eventualmente tenga una cantidad finita de combinaciones; No estoy seguro de cuántos serían: p

Con uno de mis proyectos usé las columnas:

[uInt] business_id, [uTinyInt] día, [char (11)] timeRange

Si desea admitir OpeningHoursSpecification, deberá agregar validFrom y validThrough.

El intervalo de tiempo tiene el formato: hh: mm-hh: mm

Aquí hay una función que lo analiza, también puede modificar esta función para analizar solo una apertura / cierre, si las mantiene como columnas separadas en la base de datos.

De acuerdo con mi experiencia, recomendaría que permita varias veces en un día, permita una forma de saber si están cerradas explícitamente ese día o si están abiertas las 24 horas del día, los 7 días de la semana. El mío me dijo que si faltaba un día en la base de datos, el negocio se cerraba ese día.

/**
 * parseTimeRange
 * parses a time range in the form of
 * '08:55-22:00'
 * @param $timeRange 'hh:mm-hh:mm' '08:55-22:00'
 * @return mixed ['hourStart'=>, 'minuteStart'=>, 'hourEnd'=>, 'minuteEnd'=>]
 */
function parseTimeRange($timeRange)
{
    // no validating just parsing
    preg_match('/(?P<hourStart>\d{1,2}):(?P<minuteStart>\d{2})-(?P<hourEnd>\d{1,2}):(?P<minuteEnd>\d{2})/', $timeRange, $matches);

    return $matches;
}
CTS_AE
fuente
1

La mayoría de los resultados funcionan bien para el escenario dado, pero no será tan efectivo si tiene períodos que duran varios días, por ejemplo. 8:00 AM ~ 2:00 AM, luego recomiendo usar un diseño de períodos múltiples.

   0: [
         id: 1,
         business_id: 1,
         open: true,
         day: 1,
         periods: [
             0: { open: 08:00, close: 23:59 }
         ]
      ],
   1: [
         id: 2,
         business_id: 1,
         open: true,
         day: 2,
         periods: [
             0: { open: 00:00, close: 02:00 }
             1: { open: 08:00, close: 23:59 }
         ]
      ]
Mahmoud Ali Kassem
fuente
0

Podría pensar en tener en cuenta los días festivos al incluir campos adicionales para el mes del año / día del mes / semana del mes. La semana del mes tiene algunas sutilezas menores "última" podría ser, por ejemplo, la semana 4 o 5, según el año.

Einstein
fuente