Зберігання робочого часу в базі даних


84

Наразі я намагаюся знайти найкращий спосіб зберегти години роботи компанії у базі даних.

Наприклад:

Бізнес А має наступні години роботи

  • Понеділок: 9:00 - 17:00
  • Вівторок: 9:00 - 17:00
  • Середа: 9:00 - 17:00
  • Четвер: 9:00 - 17:00
  • П’ятниця: 9:00 - 17:00
  • Субота: 9:00 - 12:00
  • Неділя: вихідний

На даний момент я маю модель даних, подібну до наведеної нижче

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

де "день" обмежений вибором 7 днів тижня в коді (через ORM). Щоб перевірити, чи компанія закрита в певний день, вона перевіряє, чи є значення open_time та close_time NULL. Це пов’язано з бізнесом через проміжну таблицю (Відносини багато до багатьох).

Чи є у когось пропозиції щодо цієї схеми бази даних? Щось у цьому мені не здається правильним.


4
Навіщо потрібні співвідношення M-to-M між таблицею business_hours та таблицею business? Якщо ви дійсно збираєтеся змусити кілька підприємств спільно використовувати один і той же запис у business_hours, чому? Семантично той факт, що "компанія C працює від T1 до T2 в день D", скоріше є об'єктом вартості, а не суттю ... Єдиною вагомою (?) Причиною, яку я можу уявити, є оптимізація розміру сховища (RDB-версія Flyweight pattern, так би мовити) якщо кількість підприємств очікується величезна ...
Ярік,

2
А як щодо обідніх перерв? А як щодо державних свят?
Моуг каже, що відновити Моніку

Відповіді:


56

Загалом, я не бачу в цьому нічого поганого. Крім ...

  1. Я б зберігав день тижня як ціле число, використовуючи будь-яку систему нумерації, яку використовує ваша рідна мова програмування (у її бібліотеках). Це зменшить розмір бази даних і видалить порівняння рядків із вашого коду.

  2. Можливо, я помістив би зовнішній ключ до бізнес-таблиці прямо тут, у цій таблиці. Таким чином, вам не знадобиться таблиця посилань.

Тому, мабуть, я б зробив:

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
)

У своїй бізнес-логіці я б застосував обмеження, згідно з яким кожен "бізнес" має щонайменше 7 "робочих годин". ( Хоча б тому, що Джон Скіт має рацію, можливо, вам захочеться святковий час.) Хоча ви, можливо, захочете послабити це обмеження, просто відклавши "робочий час" на дні, коли бізнес закритий.


1
Як би ви зробили 2? Маючи всі записи для всіх підприємств, навіть коли день / час однакові?
Вінко Врсалович

7
Я б сказав так. В іншому випадку, подумайте, що могло б статися, якщо два підприємства поділять однакові години, але тоді одному з них потрібно змінити робочий час. Вам доведеться виявити той факт, що години змінено, або створити новий запис, або відредагувати існуючий, залежно від того, спільний чи ні.
Ерік Форбс,

3
Я б абсолютно не ділив рядки цієї таблиці між підприємствами. Такий обмін просто призводить до болю. Бізнес-логіка програми повинна просто застосовувати обмеження щодо того, що кожен бізнес має щонайменше 7 посилань "business_hours".
Френк Крюгер

1
@Vinko: Пам'ятайте, що хоча значення (на даний момент) однакові, години роботи двох підприємств семантично різняться.
Draemon

1
Ви також можете використовувати растрове зображення 7 біт для "дня". Таким чином, вам не потрібно повторювати однакові записи. Приклад, усі робочі дні мають однаковий робочий час, достатньо одного запису.
ZolaKt

28

Однією з ситуацій, яка не охоплена цією схемою, є кілька періодів відкриття на день. Наприклад, місцевий паб працює з 12:00 до 14:30 та з 17:00 до 23:00.

Можливо, каси театру відкриті для урочистості та вечірньої вистави.

На цьому етапі вам потрібно вирішити, чи можете ви мати кілька записів за один день, чи вам потрібно представляти різні години в одному рядку.

А як щодо робочого часу, що перетинається опівночі. Скажімо, бар працює 19:00 - 02:00. Ви не можете просто порівняти час відкриття та закриття з часом, який ви хочете протестувати.


У підсумку я зробив щось на зразок @JordanFeldstein. Я зберігаю масиви "зміни стану", які включають зміни (відкриті чи закриті), день тижня та час доби. Я можу мати стільки "змін", скільки хочу для кожного бізнесу та кожного дня. Бізнес може відкритись одного дня, а наступний - двічі (або x разів) на день, бути відкритим цілодобово, але не працювати в понеділок. Це складно реалізувати, але дуже гнучко.
ironcito

Дотримуючись підходу найкращої відповіді, я волів би додати два стовпці до business_hours: break_timeта break_duration. Насправді рідко можна зробити 2 перерви в один день.
Фрондор,

12

Це як би залежить від того, для чого вам потрібно їх зберігати, і як можуть виглядати реальні дані.
Якщо вам потрібно мати можливість визначити, чи відкрито підприємство в певний момент, можливо, буде трохи незручно запитувати схему, як викладено. Однак, що важливіше, таке: чи не потрібно було б коли-небудь задовольнити закриття в середині дня?

Деякі варіанти включають;

  • Схема, подібна до вашої, але з можливістю мати кілька періодів на один день. Це могло б задовольнити обідню перерву, але було б незручно запускати запит, який надає вам години роботи на даний день, скажімо для презентації користувачеві.
  • Підхід до растрового стилю; "000000000111111110000000" для 9-5. Недоліком цього підходу є те, що вам потрібно вибрати конкретну деталізацію, тобто цілі години, півгодини чи, дійсно, хвилини. Чим дрібніша деталізація, тим важче читати дані для людини. Ви можете використовувати побітові оператори, щоб зберегти це значення як одне число, а не як цілий ряд, але знову це шкодить читабельності.

11

Я дізнався, що якщо ви хочете, щоб розмітка даних Google розпізнавала ваші дані, вам слід дотримуватися наступних вказівок:

https://schema.org/openingHours

http://schema.org/OpeningHoursSpecification Містить "дійсні дати", що дуже корисно для деяких підприємств.

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

З первинним ключем у вас повинно бути добре, якщо ви не дозволяєте компаніям спільно використовувати однакові години з таблицею об’єднання - цікаво, що зрештою у вас буде кінцева кількість комбінацій; Я не впевнений, скільки це було б: с

В одному зі своїх проектів я використовував колонки:

[uInt] business_id, [uTinyInt] день, [char (11)] timeRange

Якщо ви хочете підтримати OpeningHoursSpecification, вам потрібно буде додати validFrom і validThrough.

Діапазон часу має такий формат: hh: mm-hh: mm

Ось функція, яка аналізує її, ви також можете змінити цю функцію, щоб проаналізувати лише одне відкриття / закриття, якщо ви зберігаєте їх як окремі стовпці в БД.

З мого досвіду я рекомендую вам дозволити кілька разів протягом дня, дозволити спосіб визначити, чи вони явно закриті в цей день, чи відкриті цілодобово або цілодобово. Я сказав, що якщо в БД не вистачає дня, то в той день компанія була закрита.

/**
 * 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;
}

1

Більшість результатів добре спрацьовує для даного сценарію, але це не буде настільки ефективно, якщо у вас є періоди, які тривають протягом декількох днів, наприклад. 8:00 - 02:00, тоді я рекомендую використовувати багатоперіодний дизайн.

   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 }
         ]
      ]

0

Можна подумати про факторинг у святкові дні, включивши додаткові поля за місяць року / день місяця / тиждень місяця. Тиждень місяця має кілька незначних тонкощів "останнім", наприклад, може бути тиждень 4 або 5 залежно від року.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.