Дизайн базы данных цены за дату для системы бронирования отелей (сохранение каждой даты вместо расчета во время выполнения)

У меня есть требование, похожее на этот вопрос, мне нужно цены в магазине на дату (цены меняются в зависимости от сезона), и я разработал модель ниже:

  room_calendar : 
         room_id :
         date :
         price:

Я должен хранить даты до ~ одного года, а затем запустить запрос, чтобы получить все цены для x диапазонов дат и sum() всех цен.

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

У меня есть еще один второй подход:

(дата расчета во время выполнения) Да, я знаю! это может показаться сумасшедшим

db model:
   room_calendar : 
         room_id :
         date_rule: <- store a dateutil rrule
         price:
results:
{'room_id': {'dates': <dateutil.rrule.rrule object at 0x7fa2a8e3bda0>, 'price': 100}}
{'room_id': {'dates': <dateutil.rrule.rrule object at 0x7fa2a8e3bda0>, 'price': 150}}

Затем:

dates_to_reserve = [datetime(2020, 1, 1), datetime(2020, 1, 2), datetime(2020, 1, 3)]

room_price.objects.filter(date_rule__in=dates_to_reserve)

Конечно, мне пришлось бы создать настраиваемое поле, которое поддерживает хранилище dateutil.rrule, и когда я запрашиваю его с помощью __in, нажимаю метод __contains__ dateutil.rrule.

извините, я не могу выразить все лучше, ИЗВИНИТЕ ЗА МОЙ ПЛОХОЙ АНГЛИЙСКИЙ

У вас есть что-то лучше, чем первый подход?

Есть ли смысл так увлекаться вторым подходом?

Как бы вы это сделали? хранить каждую дату или вычислять ее во время выполнения

ИЗМЕНИТЬ

Спасибо за ваш комментарий @iain-shelvington:

Насколько сложны правила, влияющие на цену, и отличаются ли они для каждого номера?

  1. Каждое правило отличается для каждой комнаты
  2. Комната имеет несколько правил
  3. Каждое правило может быть ЕЖЕМЕСЯЧНЫМ или ЕЖЕНЕДЕЛЬНЫМ, в основном

Можете ли вы привести пример номера, его базовую цену и возможную цену на каждую дату? Эж.:

Room standard > price: 150 > date_rule: from Jan 2020 weekly on weekdays until Feb 25 2020
Room standard > price: 170 > date_rule: from Jan 2020 weekly on weekends until Feb 25 2020
Room standard > price: 180 > date_rule: from Feb 2020 weekly on weekends until May 26 2020

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

Спасибо


person LuisSolis    schedule 06.07.2020    source источник
comment
Насколько сложны правила, влияющие на цену, и отличаются ли они для каждого номера? Можете ли вы привести пример номера, его базовую цену и возможную цену на каждую дату?   -  person Iain Shelvington    schedule 06.07.2020
comment
спасибо за ваш комментарий @IainShelvington, я отредактировал вопрос, пожалуйста, смотрите.   -  person LuisSolis    schedule 06.07.2020


Ответы (2)


Я использую dateutil.rrrule для приложения диспетчера задач. Я храню его в поле varchar, а затем в базе данных есть функции, написанные на plpythonu3, которые могут расширять это значение по требованию. Это один из способов.

Один из примеров такой функции:

CREATE OR REPLACE FUNCTION public.task_occurrences(t_rrule character varying, start_dt timestamp with time zone, end_dt timestamp with time zone)
 RETURNS timestamp with time zone
 LANGUAGE plpython3u
 SECURITY DEFINER
AS $function$
from datetime import datetime
from dateutil.parser import parse
from dateutil.rrule import rrulestr

rule = rrulestr(t_rrule, ignoretz=True)
next_occ = rule.between(parse(start_dt, ignoretz=True),
                        parse(end_dt, ignoretz=True), inc=True, count=1)

if next_occ:
    return next_occ[0]
else:
    return None

$function$
;

Это находит первое вхождение задачи в диапазоне дат или возвращает None/NULL, если его нет.

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

Пример:

CREATE TABLE seasonal_price(id serial, start_date date, end_date date, price numeric);

SELECT price FROM seasonal_price, room_calendar WHERE room_calendar.date <@ daterange(start_date, end_date, '[]');

person Adrian Klaver    schedule 06.07.2020
comment
Спасибо за ваш ответ, я использую dateutil и для другой цели, я нахожу первый вариант, который вы постулируете, интересным. Не могли бы вы расширить свой ответ примерами, пожалуйста? - person LuisSolis; 06.07.2020
comment
Добавлен пример функции rrule и пример Season_price. Это то, что вы ищете? - person Adrian Klaver; 06.07.2020
comment
Спасибо @AdrianKlave, ваш пример очень иллюстрирует, как обрабатывать dateutil.rrule, у меня есть что-то похожее для повторяющихся событий. Но мне нужно проверить несколько дат для нескольких правил, чтобы получить цену для каждой даты. второй пример похож на первый подход, который я упомянул в этом вопросе. еще раз спасибо - person LuisSolis; 06.07.2020

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

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

person Laurenz Albe    schedule 06.07.2020
comment
Спасибо за Ваш ответ. решение, которое он предлагает, разумно, но правила постоянно меняются и являются более сложными, чем просто диапазон дат. - person LuisSolis; 06.07.2020