日历表实现了空间/时间折衷。通过使用更多空间,某些类型的查询可以在更短的时间内运行,因为它们可以利用索引。只要您小心使用 CHECK() 约束,并且只要您有管理流程来处理您的 dbms 不支持的任何约束,它们就是安全的。
如果您的粒度是一分钟,那么您每年需要生成大约 50 万行。一个最小的日历表看起来像这样。
2011-01-01 00:00:00
2011-01-01 00:01:00
2011-01-01 00:02:00
2011-01-01 00:03:00
2011-01-01 00:04:00
如果你正在做“桶”分析,你可能会更好地使用这样的东西。
bucket_start bucket_end
--
2011-01-01 00:00:00 2011-01-01 00:01:00
2011-01-01 00:01:00 2011-01-01 00:02:00
2011-01-01 00:02:00 2011-01-01 00:03:00
2011-01-01 00:03:00 2011-01-01 00:04:00
2011-01-01 00:04:00 2011-01-01 00:05:00
由于 SQL 的 BETWEEN 运算符包含端点,因此您通常需要避免使用它。那是因为它包括端点,并且很难将 bucket_end 表示为“bucket_start 加上一分钟,减去此服务器可以识别的最小时间”。(危险是比 bucket_end 大一微秒的值,但仍小于 bucket_start 的下一个值。)
如果我要建造那张桌子,我可能会这样做。(虽然我会更加努力地考虑是否应该将其称为“日历”。)
create table calendar (
bucket_start timestamp primary key,
bucket_end timestamp unique,
CHECK (bucket_end = bucket_start + interval '1' minute)
-- You also want a "no gaps" constraint, but I don't think you
-- can do that in a CHECK constraint in PostgreSQL. You might
-- be able to use a trigger that counts the rows, and compares
-- that count to the number of minutes between min(bucket_start)
-- and max(bucket_start). Worst case, you can always run a report
-- that counts the rows and sends you an email.
);
UNIQUE 约束在 PostgreSQL 中创建一个隐式索引。
此查询将一次插入一天的行(24 小时 * 60 分钟)。
insert into calendar
select coalesce(
(select max(bucket_start) from calendar),
cast('2011-01-01 00:00:00' as timestamp)
)
+ cast((n || 'minute') as interval) as bucket_start,
coalesce(
(select max(bucket_start) from calendar),
cast('2011-01-01 00:00:00' as timestamp)
)
+ cast((n + 1 || ' minute') as interval) as bucket_end
from generate_series(1, (24*60) ) n;
您可以将其包装在一个函数中以一次生成一年。我可能会尝试一次提交少于 50 万行。
生成 2000 万行用于测试和另外 2000 万行“日历”分钟应该不会花费太长时间。长午餐。也许是在阳光下的一个下午。