如何在 SQL 数据库中存储商店营业时间?

How to store a store opening hours in an SQL database?

我正在为商店(实际上是餐馆)开发应用程序,需要为其设计 PostgreSQL 数据结构。

例如,餐厅的营业时间可能为周一至周五 7:30 至 17:00 以及第二天 20:30 至 1:00。

每个工作日要存储的数据类似于 ['Monday', true, 450, 1050, 1230, 1500 ],为真 ===“它在星期一开放”,450,开放时间是午夜过后 450 分钟(参见 here), i.e. at 7:30h, closes at 17:30h, reopens at 20:30h, and closes at 1 a.m. (split hours and close time after midnight are not at all unusual in my home country, Spain). Of course, I could dispense with the first two elements, 'Monday' and 'true', but they will probably make front-end development easier (e.g. see model for data input).

我已经确定了至少四个在 PostgreSQL 数据库中存储数据的可行选项:

1) 'restaurants' table 中的列 'opening_hours' 具有 jsonb 数据类型

[

['Monday', 真, 450, 1050, 1230, 1500 ]

...

['Sunday', 错误, 0, 0, 0, 0 ]

]

2) table 'restaurants'

中每小时一栏

我可能会省略上面显示的前两个元素('Monday' 和 'true')。这会将 7 x 4 = 28 列添加到我的 table:

3) 新 table 'opening_hours'

使用外键 'restaurant_id' 引用 table 'restaurants' 中的 'id',设计与 2) 相同。

4) 所有 7 个工作日的数据类别列

例如,列 'open1' 的格式为“0450-0450-0450-0450-0450-0000-0000”,如 here。因此,我会像选项 1) 那样汇总数据,但我看不出后者比前一个选项有任何真正的优势。

目前,选项 1 足以满足我要实现的业务逻辑:以与 Google 类似的方式显示营业时间,因此我看不出有任何理由可以继续使用2) or 3) over 1), 当然我可能会错过未来发展的可能性。

什么数据结构遵循最佳实践?还有比这些更好的选择吗?

一种非常灵活且标准化良好的方法是将每个开盘时段存储为 table 中的一行。开放期可以编码为它开始的工作日、它开始的一天中的时间和它持续的持续时间。每个营业时间都通过外键链接到一家餐厅。

CREATE TABLE opening_period
             (restaurant integer,
              weekday integer,
              time time,
              duration interval,
              PRIMARY KEY (restaurant,
                           weekday,
                           time,
                           duration),
              FOREIGN KEY (restaurant)
                          REFERENCES restaurant
                                     (id)
                          ON DELETE CASCADE,
              CHECK (weekday >= 0
                     AND weekday < 7),
              -- prevent overlapping opening periods
              EXCLUDE USING gist (restaurant WITH =,
                                  tsrange('epoch'::timestamp + time + weekday * INTERVAL '1 days',
                                          'epoch'::timestamp + time + weekday * INTERVAL '1 days' + duration,
                                          '[)') WITH &&));