Вопрос

Мы создаем приложение, в котором хранятся "часы работы" для различных предприятий.Каков самый простой способ представления этих данных, чтобы вы могли легко проверить, открыт ли элемент?

Некоторые варианты:

  • Выделяйте блоки (каждые 15 минут), которые вы можете пометить как "открыто / закрыто".Проверка включает в себя проверку того, установлен ли бит "открыто" на желаемое время (немного похоже на расписание поездов).
  • Сохранение списка временных диапазонов (с 11 утра до 14:00, с 5 до7 вечера и т.д.) И проверка того, попадает ли текущее время в какой-либо указанный диапазон (это то, что делает наш мозг при разборе приведенных выше строк).

Есть ли у кого-нибудь опыт хранения и запроса информации о расписании и какие-либо рекомендации?

(Есть всякие сумасшедшие угловые случаи вроде "закрыто в первый вторник месяца", но мы оставим это на другой день).

Это было полезно?

Решение

сохраняйте каждый непрерывный блок времени как время начала и длительность;это упрощает проверку того, когда часы пересекают границы даты

если вы уверены, что часы работы никогда не будут пересекать границы даты (т.е.никогда не будет распродажи на всю ночь или 72-часового марафона и др.), Тогда времени начала / окончания будет достаточно

Другие советы

Наиболее гибким решением может быть использование подхода с набором битов.В неделе 168 часов, то есть 672 15-минутных периода.Это всего лишь 84 байта свободного места, что должно быть терпимо.

Я бы использовал такую таблицу, как эта:

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

Здесь у нас есть бизнес, который работает по расписанию с 5 до 6 часов, но по воскресеньям работает меньше.

Запрос на if open будет следующим (псевдо-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;

Если вам нужно хранить крайние случаи, тогда просто имейте 365 записей, по одной в день ... на самом деле это не так уж много в общей схеме вещей, поместите индекс в столбец day и BusinessID.

Не забудьте сохранить часовой пояс предприятий в отдельной таблице (нормализовать!) и выполнить преобразование между вашим временем и ит, прежде чем проводить эти сравнения.

Я думаю, что лично я бы выбрал время начала и окончания, так как это сделало бы все более гибким.Хорошим вопросом было бы:какова вероятность того, что размер блока изменится в определенный момент?Затем выберите решение, которое наилучшим образом соответствует вашей ситуации (если оно может измениться, я бы определенно выбрал временные интервалы).

Вы могли бы сохранить их как временной интервал и использовать сегменты в своем приложении.Таким образом, у вас есть простой ввод с использованием блоков, сохраняя при этом гибкость для изменений в вашем хранилище данных.

Чтобы добавить к этому Джонатана Холланда сказал, Я бы допустил несколько записей за один и тот же день.

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

Почему?многие рестораны и некоторые предприятия, а также многие предприятия по всему миру устраивают перерывы на обед и/или вторую половину дня.Кроме того, многие рестораны (2, которые я знаю рядом с моим домом) закрываются в нечетное время, отличное от 15 часов.Один закрывается в 9:40 вечера по воскресеньям, а другой - в 1:40 ночи.

Существует также проблема с праздничными часами, такими как, например, раннее закрытие магазинов в день благодарения, поэтому вам необходимо переопределить их на основе календаря.

Возможно, что можно сделать, так это открыть дату / время, закрыть дату-время, например, так:

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

и т.д.(тип:1 открыто и 0 закрыто)

И пусть все дни в ближайшие 1 или два года будут предварительно рассчитаны на 1-2 года вперед.Обратите внимание, что у вас будет только 3 столбца:int, дата / время / бит, поэтому потребление данных должно быть минимальным.

Это также позволит вам изменять определенные даты для нечетных часов в особые дни по мере их появления.

Он также заботится о переходе через полночь, а также о переходе через 12/24 часа.

Он также не зависит от часового пояса.Если вы сохраняете время начала и продолжительность, то при расчете времени окончания ваш компьютер выдаст вам скорректированное время TZ?Это то, чего ты хочешь?Еще код.

что касается запроса статуса открыто-закрыто:запросите соответствующую дату и время,

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

затем посмотрите на "тип".если один, то он открыт, если 0, то закрыт.

PS:Я работал в розничной торговле 10 лет.Так что я знаком с проблемами сумасшедшего рабочего дня в малом бизнесе.

Хорошо, я расскажу об этом, чего бы это ни стоило.

Мне нужно уладить довольно много дел.

  • Быстрый / Производительный запрос
  • Любые промежутки времени, 9:01 вечера, 12:14 и т.д.
  • Международный (?) - не уверен, что это проблема даже с часовыми поясами, по крайней мере, в моем случае, но кто-то более сведущий здесь, не стесняйтесь вмешаться
  • Открыто - закрывается до следующего дня (открыто в полдень, закрывается в 2:00 ночи)
  • Несколько временных интервалов в день
  • Возможность переопределять определенные дни (праздники, что угодно)
  • Возможность повторяющихся переопределений
  • Возможность запрашивать информацию о любом моменте времени и открывать предприятия (сейчас, в будущем, в прошлом)
  • Возможность легко исключить результаты того, что предприятия закрываются в ближайшее время (фильтруйте предприятия, закрывающиеся через 30 минут, вы же не хотите, чтобы ваши пользователи были "тем парнем", который появляется за 5 минут до закрытия в индустрии продуктов питания и напитков).

Мне нравятся многие представленные подходы, и я заимствую некоторые из них.На моем веб-сайте, в проекте, во всем, что мне нужно учитывать, у меня могут быть миллионы компаний, и некоторые из приведенных здесь подходов лично мне кажутся недостаточно масштабируемыми.

Вот что я предлагаю в качестве алгоритма и структуры.

Мы должны сделать некоторые конкретные предположения по всему миру, в любом месте и в любое время:В неделе всего 7 дней.В одном дне 1440 минут.Существует конечное число возможных перестановок минут открытия / закрытия.

Не конкретные, но достойные предположения:Многие перестановки открытых / закрытых минут будут распределены между предприятиями, что сократит общее количество фактически сохраненных перестановок.Было время в моей жизни, когда я мог легко рассчитать фактические возможные комбинации этого подхода, но если бы кто-то мог помочь / подумал, что это было бы полезно, это было бы здорово.

Я предлагаю 3 таблицы:Прежде чем прекратить чтение, учтите, что в реальном мире 2 из этих таблиц будут достаточно аккуратно кэшироваться небольшим размером.Этот подход также не подойдет всем из-за огромной сложности кода, необходимого для преобразования пользовательского интерфейса в модель данных и обратно, если это необходимо.Ваш пробег и потребности могут отличаться.Это попытка разумного решения уровня "предприятия", что бы это ни значило.

Таблица часов выполнения операций

ИДЕНТИФИКАТОР | ОТКРЫТО (минута в минуту) | ЗАКРЫТО (минута в минуту)


1 | 360 | 1020 (пример:9 УТРА - 5 вечера)

2 | 365 | 1021 (пример:крайний случай 9:05 утра - 17:01 вечера (чудаки) )

и т.д.

HoursOfOperations не заботится о том, в какие дни, просто открывает, закрывает и сохраняет уникальность.На комбинацию открытия/закрытия может быть только один вход.Теперь, в зависимости от вашей среды, либо вся эта таблица может быть кэширована, либо она может быть кэширована для текущего часа дня и т.д.Во всяком случае, вам не нужно запрашивать эту таблицу для каждой операции.В зависимости от вашего решения для хранения данных я предполагаю, что каждый столбец в этой таблице будет проиндексирован для повышения производительности.С течением времени эта таблица, вероятно, имеет экспоненциально обратную вероятность ВСТАВКИ (ов).На самом деле, работа с этой таблицей в основном должна быть внутрипроцессной операцией (RAM).

Бизнес-карта 2hoursmap

Примечание:В моем примере я сохраняю "День" как поле / столбец с битовым флагом.Во многом это связано с моими потребностями и продвижением перечислений LINQ / Flags в C #.Ничто не мешает вам расширить это поле до 7-битных полей.Оба подхода должны быть относительно схожими как в логике хранения, так и в подходе к запросам.

Еще Одна записка:Я не вступаю в семантический спор по поводу "каждой таблице нужен столбец PK ID", пожалуйста, найдите для этого другой форум.

BusinessID | HoursID | Day (или, если вы предпочитаете, разделить на:НЕМНОГО Понедельника, НЕМНОГО вторника, ...)


1 | 1 | 1111111 (это заведение открыто с 9 до 5 каждый день недели)

2 | 2 | 1111110 (это заведение открыто с 9:05 до 5:01 Мск-Сб (понедельник = день 1)

Причина, по которой этот запрос легко выполнить, заключается в том, что мы всегда можем довольно легко определить MOTD (минуту дня), которая нам нужна.Если я хочу знать, что открыто завтра в 5 часов вечера, я беру все идентификаторы HoursOfOperations, ГДЕ Close > = 1020.Если я не ищу временной диапазон, Open становится незначительным.Если вы не хотите показывать, что предприятия закрываются в ближайшие полчаса, просто соответствующим образом скорректируйте время входа (ищите 17:30 вечера (1050), а не 17:00 вечера (1020).Второй запрос, естественно, был бы "дайте мне все дела с HoursID В (1, 2, 3, 4, 5), и т.д.Вероятно, это должно вызвать тревогу, поскольку у такого подхода есть ограничения.Однако, если кто-то сможет ответить на вопрос о перестановках, приведенный выше, мы, возможно, сможем снять красный флажок.Предположим, что нам нужны только возможные перестановки в любой части уравнения одновременно, либо открытые, либо закрытые.

Учитывая, что наша первая таблица кэширована, это быстрая операция.Вторая операция запрашивает эту таблицу с потенциально большими строками, но мы ищем очень маленькие (SMALLINT), надеюсь, индексированные столбцы.

Теперь, возможно, вы видите сложность на стороне кода.В моем конкретном проекте я ориентируюсь в основном на бары, поэтому можно с уверенностью предположить, что у меня будет значительное количество заведений с таким графиком работы, как "11:00 - 2: 00 ночи (на следующий день)".Это действительно было бы 2 записи как в таблице HoursOfOperations, так и в таблице Business2HoursMap.Например.бар, который открыт с 11:00 до 2:00 ночи, будет иметь 2 ссылки на таблицу часов работы с 660 по 1440 (с 11:00 до полуночи) и с 0 по 120 (с полуночи до 2:00 ночи).Эти ссылки будут отражены в фактических днях в таблице Business2HoursMap в виде 2 записей в нашем упрощенном случае, 1 запись = ссылка на часы всех дней # 1, другая ссылка на все дни # 2.Надеюсь, в этом есть смысл, ведь это был долгий день.

Переопределение в особые дни / праздничные дни / что угодно.Переопределения по своей природе основаны на дате, а не на дне недели.Я думаю, что именно здесь некоторые подходы пытаются засунуть пресловутый круглый колышек в квадратное отверстие.Нам нужен еще один столик.

Номер рабочего дня | BusinessID / День | Месяц | Год

1 | 2 | 1 | 1 | НОЛЬ

Это, конечно, может усложниться, если вам нужно что-то вроде "каждый второй вторник эта компания отправляется на рыбалку на 4 часа".Однако, что это позволит нам сделать довольно легко, так это разрешить 1 - переопределения, 2 - разумные повторяющиеся переопределения.Например,Дж.если значение year РАВНО НУЛЮ, то каждый год в день Нового года этот бар weirdo открыт с 9:00 утра до 17:00 вечера в соответствии с приведенными выше примерами данных.То есть.- Если и был установлен год, то только на 2013-й.Если месяц равен нулю, то это каждый первый день месяца.Опять же, это не позволит обрабатывать каждый сценарий планирования только по нулевым столбцам, но теоретически вы могли бы обрабатывать практически все, полагаясь при необходимости на длинную последовательность абсолютных дат.

Опять же, я бы кэшировал эту таблицу на постоянной основе.Я просто не могу реально представить, что строки для этой таблицы в моментальном снимке за один день будут очень большими, по крайней мере, для моих нужд.Я бы сначала проверил эту таблицу, поскольку она является переопределением, и сохранил бы запрос к гораздо более крупной таблице Business2HoursMap на стороне хранилища.

Интересная проблема.Я действительно удивлен, что это первый раз, когда мне действительно нужно все хорошенько обдумать.Как всегда, очень интересуюсь различными идеями, подходами или недостатками в моем подходе.

Сегментные блоки лучше, просто убедитесь, что вы предоставляете пользователю простой способ их установки.Щелчок и перетаскивание - это хорошо.

Любая другая система (например, диапазоны) будет действительно раздражать, когда вы пересечете границу полуночи.

Что касается того, как вы их храните, то в C ++ битовые поля, вероятно, были бы лучшими.На большинстве других языков, и array мог бы быть лучше (много потраченного впустую места, но работал бы быстрее и был бы легче для понимания).

Я бы немного подумал об этих крайних случаях прямо сейчас, потому что они собираются сообщить, есть ли у вас базовая конфигурация плюс наложение или полное статическое хранение времени открытия или что-то еще.

Существует так много исключений - и на регулярной основе (например, снежные дни, нерегулярные праздники, такие как Пасха, Страстная пятница), что, если ожидается, что это будет надежным представлением реальности (в отличие от правильного предположения), вам нужно будет довольно скоро обратиться к этому в архитектуре.

Как насчет чего-то вроде этого:

Таблица часов хранения

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

'Condition' - это лямбда-выражение (текст для предложения 'where').Постройте запрос динамически.Таким образом, для конкретного бизнеса вы выбираете все время открытия / закрытия

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)

Итак, завершите тем, чего вы хотите, чем-то вроде:

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

Если результат последнего запроса равен 1, то хранилище открыто в момент времени t, в противном случае оно закрыто.

Теперь вам просто нужен дружественный интерфейс, который может генерировать ваши предложения where (лямбда-выражения) для вас.

Единственный другой пример, о котором я могу вспомнить, - это то, что происходит, если магазин открыт, скажем, с 7 утра до 2 часов ночи в один день, но закрывается в 11 вечера следующего дня.Ваша система должна быть в состоянии справиться и с этим, разумно распределив время между двумя днями.

Здесь, конечно, нет необходимости экономить память, но, возможно, необходим чистый и понятный код."Немного передергивать" - это, ИМХО, не тот путь, которым следует идти.

Здесь нам нужен контейнер set, который содержит любое количество уникальных элементов и может быстро и легко определить, является ли элемент участником или нет.Настройка требует осторожности, но при обычном использовании одна строка простого для понимания кода определяет, открыты вы или закрыты

Концепция:Присваивайте индексный номер каждому 15-минутному блоку, начиная, скажем, с полуночи воскресенья.

Инициализировать:Вставьте в набор порядковый номер каждого 15-минутного блока, когда вы открыты.(Предполагая, что вы открыты меньше часов, чем закрыты.)

Использование:Вычтите из интересного времени в минутах полночь предыдущего воскресенья и разделите на 15.Если этот номер присутствует в наборе, вы открыты.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top