PostgreSQL 9 中的日历表
Posted
技术标签:
【中文标题】PostgreSQL 9 中的日历表【英文标题】:Calendar tables in PostgreSQL 9 【发布时间】:2011-04-28 16:28:40 【问题描述】:我正在构建一个分析数据库(我对数据和业务目标有深刻的理解,并且只有基本到中级的数据库技能)。
我遇到过一些关于构建类似仓库的参考资料,这些仓库实现了“日历表”的概念。这是有道理的,而且很容易做到。但是,我看到的大多数示例都是将范围限制为“天”的日历表。我的数据需要分析到小时级别。可能是几分钟。
我的问题:在空间效率和查询/排序速度方面,实现小时/分钟级别粒度的日历表是否有价值?如果是这样,您能推荐一个表结构和填充方法/示例吗?
我的主数据表在任何给定时间都将包含 20+ 百万行数据,并且用于分析的典型子集在 1 到 500 万之间。所以,如您所见,这是很多时间戳字段。
【问题讨论】:
【参考方案1】:在PostgreSQL
中,您可以即时生成任意长度和粒度的日历表:
SELECT CAST('2011-01-01' AS DATE) + (n || ' hour')::INTERVAL
FROM generate_series(0, 23) n
这不需要递归(与其他系统一样)并且是生成易失性结果集的首选方法。
【讨论】:
是的,但是在大约 2000 万行上加入 generate_series() 的结果可能会拖累性能。日历表上的查询可以利用索引。 @Catcall:对日历表的查询通常假定日历表中的所有值与事实表中的某些值之间存在左连接,因此对于这些期间返回NULL
记录没有事实记录。您能否提供一个示例查询,将generate_series
替换为带有数据的实际表?
有趣。谢谢你。所以我的想法是,我可以创建三个这样的表:天、小时、分钟,并且我的数据集表将具有诸如“day_id”、“hour_id”、“minute_id”之类的键,它们可以一起使用或独立使用,具体取决于分辨率我的分析?如果是这样,那就太棒了。如果没有,我错过了一些东西。
@NJ:为什么,您可以直接在generate_series
中生成分钟分辨率。请写一个你想要做的查询,我会告诉你如何与generate_series
配对。
@NJ 通常你只有一个非规范化的日历表和你的事实表中的一个键。在这种情况下为 minute_id,因为这是您日历的粒度。你的日历表会有例如一个月一栏,一天一栏,一分钟一栏。要获取一天的所有数据,请执行select ... from calendar c left join datatable d on c.id = d.minute_id where c.day='2011-04-28'
。要获取一分钟的数据,您需要 select ... from calendar c left join datatable d on c.id = d.minute_id where c.minute='2011-04-28 04:10:00'
【参考方案2】:
日历表实现了空间/时间权衡。通过使用更多空间,某些类型的查询可以在更短的时间内运行,因为它们可以利用索引。只要您小心使用 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 万行“日历”分钟应该不会花费太长时间。长午餐。也许是一个阳光下的下午。
【讨论】:
你在说什么“索引的优势”?【参考方案3】:在我构建的数据仓库中,我使用了单独的 CALENDAR 和 TIME_OF_DAY 维度。第一个维度的粒度为 1 天,第二个维度为 1 分钟。
在另外两种情况下,我事先知道在小于 15 分钟的粒度上不需要报告。在这种情况下,为简单起见,我使用了一个 CALENDAR 维度,每天有 96 条记录。
到目前为止,我在 Oracle 仓库中使用了这种方法,但今年夏天我可能会参与一个 PostgreSQL 仓库项目。
【讨论】:
以上是关于PostgreSQL 9 中的日历表的主要内容,如果未能解决你的问题,请参考以下文章
postgres安装数据库集群初始化失败(Postgresql Version 9.4.4)
PostgreSQL 9.4.2 中的“CREATE TRIGGER”使用啥锁(如果有)
如何在Debian 8/7上安装PostgreSQL 9.6
PostgreSQL:pg_hba.conf中的MD5身份验证给我致命:用户“ postgres”的对等身份验证失败]] << [