我有一个关于在数据库中存储星期和时间的两部分问题。我使用的是Rails 4.0,Ruby 2.0.0和Postgres。
我有某些事件,这些事件都有一个时间表。例如,对于活动" Skydiving",我可能有周二,周三和下午3点。
wday
方法的作用。 任何建议都会非常有用。
答案 0 :(得分:9)
我有没有办法存储周二和周二的记录 星期三连续或我应该有两个记录吗?
有多种方法可以在一行中存储多个时间范围。 @bma已经提供了其中几个。这可能对于使用非常简单的时间模式节省磁盘空间很有用。干净,灵活和“规范化”的方法是每个时间范围存储一行。
存储日期和时间的最佳方式是什么?
使用 timestamp
(或timestamptz
,如果您必须处理多个时区)。选择任意“暂存”周,并在使用timestamp
的日期和时间方面时忽略日期部分。我的经验最简单,最快速,并且所有与日期和时间相关的完整性检查都是自动内置的。我使用以1996-01-01 00:00
开头的范围用于几个类似的应用程序,原因有两个:
sun = 7
)一致。由于您实际上处理时间范围(而不仅仅是“日期和时间”),我建议您使用built-in range type tsrange
(或tstzrange
)。一个主要优点:您可以使用内置Range Functions and Operators的武器库。需要Postgres 9.2或更高版本。
例如,你可以建立一个排除约束(通过一个可以提供额外好处的全功能GiST索引在内部实现),以排除重叠的时间范围。有关详细信息,请考虑此相关答案:
对于此特定排除约束(没有重叠范围每个事件),您需要在约束中包含整数列event_id
,因此您需要安装附加模块{{3 }}。每个数据库安装一次:
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
);
对于每周时间表,请使用前七天,周一至周日或任何适合您的人。每月或每年的时间表以类似的方式。
btree_gist在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;
{p> ISODOW
代替DOW
,SQL Fiddle.代替7
,而不是0
。有很多你可以提取的内容。
此相关答案演示了如何使用范围类型运算符计算时间范围的总持续时间(上一章):
答案 1 :(得分:3)
查看ice_cube gem(link)。
它可以为您创建一个可以持久保存到数据库的计划对象。您无需创建两个单独的记录。对于第二部分,您可以根据任何规则创建计划,您无需担心如何将其保存在数据库中。您可以使用gem提供的方法从持久化的计划对象中获取所需的任何信息。
答案 2 :(得分:3)
根据您的日程安排需求的复杂程度,您可能需要查看RFC 5545 iCalendar日程安排数据格式,以获取有关如何存储数据的建议。
如果你需要的话很简单,那可能就是矫枉过正。 Postgresql有许多functions to convert date and time到你需要的任何格式。
对于存储相对日期和时间的简单方法,您可以将星期几存储为建议的整数,将时间存储为TIME
数据类型。如果您可以在一周中有多天有效,则可能需要使用ARRAY。
例如
[编辑:添加一些简单的例子]
/* Custom the time and timetz range types */
CREATE TYPE timerange AS RANGE (subtype = time);
--drop table if exists schedule;
create table schedule (
event_id integer not null, /* should be an FK to "events" table */
day_of_week integer[],
time_of_day time,
time_range timerange,
recurring text CHECK (recurring IN ('DAILY','WEEKLY','MONTHLY','YEARLY'))
);
insert into schedule (event_id, day_of_week, time_of_day, time_range, recurring)
values
(1, ARRAY[1,2,3,4,5]::INTEGER[], '15:00:00'::TIME, NULL, 'WEEKLY'),
(2, ARRAY[6,0]::INTEGER[], NULL, '(08:00:00,17:00:00]'::timerange, 'WEEKLY');
select * from schedule;
event_id | day_of_week | time_of_day | time_range | recurring
----------+-------------+-------------+---------------------+-----------
1 | {1,2,3,4,5} | 15:00:00 | | WEEKLY
2 | {6,0} | | (08:00:00,17:00:00] | WEEKLY
第一个条目可以理解为:该活动在周一至周五下午3点有效,每周都有这样的时间表。
第二个条目可以理解为:该活动周六和周日上午8点至下午5点有效,每周发生。
自定义范围类型“时间范围”用于表示时间范围的下边界和上边界 '('表示“包含”,尾随']表示“独占”,或换句话说“大于或等于8am且小于5pm”。
答案 3 :(得分:1)
为什么不只是存储日期戳,然后使用Date
的内置功能来获取星期几?
2.0.0p247 :139 > Date.today
=> Sun, 10 Nov 2013
2.0.0p247 :140 > Date.today.strftime("%A")
=> "Sunday"
strftime
听起来它可以为你做一切。 Here是针对它的具体文档。
特别是对于您所谈论的内容,听起来您需要一个Event
表has_many :schedules
,其中Schedule
会有start_date
个时间戳。 ..