酒店预订系统的按日期价格数据库设计(存储每个日期与在运行时计算)

Price per date database design for hotel reservation system (store each date vs calcul it at runtime)

我有一个类似于 this question 的要求,我需要存储每个日期的价格(价格随季节变化),我设计了波纹管模型:

  room_calendar : 
         room_id :
         date :
         price:

我必须存储大约一年的日期,然后 运行 一个查询以获取 x 个日期范围的所有价格和 sum() 所有价格。

我觉得这个方法不错,缺点是数据库中的数据量大,数据库记录多的时候查询性能不佳。

我想到了另一种方法:

(calcul date at runtime) Yes I know! it may sound crazy

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 查询它时,请点击 dateutil.rrule[= 的 __contains__ 方法21=]

抱歉我无法用更好的方式表达一切,抱歉我的英语不好

你有比第一种方法更好的方法吗?

参与第二种方法有什么意义吗?

你会怎么做?存储每个日期或在 运行time

计算它

编辑

感谢您的评论@iain-shelvington

影响价格的规则有多复杂?每个房间的规则是否不同?

  1. 每个房间的规则都不一样
  2. 一个房间有多个规则
  3. 每个规则可能是每月或每周,主要是

你能举个房间的例子吗?它的基本价格和每个日期的最终价格? Ej.:

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 的应用程序,可以正确处理更复杂的规则,所以规则不会有问题。 编写一个在 运行 时间按日期计算价格的高效算法将是问题所在

谢谢

我将 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$
;

这会找到某个日期范围内任务的第一次出现,如果没有,则 returns None/NULL。

另一种选择是将季节性价格存储在单独的 table 中,其中包含季节的开始和结束日期以及相关价格。您可以使用 Postgres 日期范围数学 range types, range operators 将 table 加入房间日期以获取适当的价格。

示例:

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, '[]');

价格计算规则往往会发生变化,因此我会避免在数据库中对其进行硬编码。

与其存储每一天的价格,不如使用 daterange 类型来保存整个日期范围内的价格。