有没有办法让我将周二和周三的记录存储在一行中,或者我应该有两条记录吗?
有几种方法可以在一行中存储多个时间范围。@bma 已经提供了其中的几个。这对于使用非常简单的时间模式节省磁盘空间可能很有用。干净、灵活和“规范化”的方法是每个时间范围存储一行。
存储日期和时间的最佳方式是什么?
使用 a timestamp
(或者timestamptz
如果可能涉及多个时区)。选择一个任意的“暂存”周,并在使用timestamp
. 根据我的经验,最简单和最快的,所有与日期和时间相关的完整性检查都是自动内置的。我将一个范围从1996-01-01 00:00
几个类似的应用程序开始使用,原因有两个:
- 一周的前 7 天与一个月中的某一天重合(对于
sun = 7
)。
- 同时也是最近的闰年(年度模式为 2 月 29 日)。
范围类型
由于您实际上是在处理时间范围(不仅仅是“日期和时间”),我建议使用内置范围类型tsrange
(或tstzrange
)。一个主要优势:您可以使用内置的Range Functions 和 Operators库。需要 Postgres 9.2 或更高版本。
例如,您可以在此基础上建立一个排除约束(通过可能提供额外好处的全功能 GiST 索引在内部实现),以排除重叠的时间范围。有关详细信息,请考虑此相关答案:
对于这个特定的排除约束(每个事件没有重叠范围),您需要event_id
在约束中包含整数列,因此您需要安装附加模块btree_gist。每个数据库安装一次:
CREATE EXTENSION btree_gist; -- once per db
或者,您可以有一个简单的CHECK
约束来使用“范围包含在”运算符来限制允许的时间段<@
。
可能看起来像这样:
CREATE TABLE event (event_id serial PRIMARY KEY, ...);
CREATE TABLE schedule (
event_id integer NOT NULL REFERENCES event(event_id)
ON DELETE CASCADE ON UPDATE CASCADE
, t_range tsrange
, PRIMARY KEY (event_id, t_range)
, CHECK (t_range <@ '[1996-01-01 00:00, 1996-01-09 00:00)') -- restrict period
, EXCLUDE USING gist (event_id WITH =, t_range WITH &&) -- disallow overlap
);
对于每周计划,请使用前 7 天、周一至周日或任何适合您的时间。以类似的方式按月或按年计划。
如何提取星期几、时间等?
@CDub在 Ruby 端提供了一个模块来处理它。我无法对此发表评论,但你也可以在 Postgres 中做任何事情,而且性能无可挑剔。
SELECT ts::time AS t_time -- get the time (practically no cost)
SELECT EXTRACT(DOW FROM ts) AS dow -- get day of week (very cheap)
或以类似的方式用于范围类型:
SELECT EXTRACT(DOW FROM lower(t_range)) AS dow_from -- day of week lower bound
, EXTRACT(DOW FROM upper(t_range)) AS dow_to -- same for upper
, lower(t_range)::time AS time_from -- start time
, upper(t_range)::time AS time_to -- end time
FROM schedule;
db<>fiddle here
旧的 sqliddle
ISODOW
DOW
而不是EXTRACT()
退货7
而不是0
星期天。您可以提取的内容有一长串。
这个相关答案演示了如何使用范围类型运算符来计算时间范围的总持续时间(最后一章):