14

我正在构建一个分析数据库(我对数据和业务目标有深刻的理解,并且只有基本到中等的数据库技能)。

我遇到了一些关于构建类似仓库的参考资料,这些仓库实现了“日历表”的概念。这是有道理的,而且很容易做到。但是,我看到的大多数示例都是将范围限制为“天”的日历表。我的数据需要分析到小时级别。可能几分钟。

我的问题:在空间效率和查询/排序速度方面,小时/分钟级别粒度的日历表的实现是否有价值?如果是这样,您能推荐一个表结构和填充方法/示例吗?

我的主数据表在任何给定时间都将包含 20+ 百万行数据,并且用于分析的典型子集在 1 到 500 万之间。所以,如您所见,这是很多时间戳字段。

4

3 回答 3

18

PostgreSQL中,您可以即时生成任意长度和粒度的日历表:

SELECT  CAST('2011-01-01' AS DATE) + (n || ' hour')::INTERVAL
FROM    generate_series(0, 23) n

这不需要递归(与其他系统一样),并且是生成易失性结果集的首选方法。

于 2011-04-28T16:34:57.463 回答
11

日历表实现了空间/时间折衷。通过使用更多空间,某些类型的查询可以在更短的时间内运行,因为它们可以利用索引。只要您小心使用 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 万行“日历”分钟应该不会花费太长时间。长午餐。也许是在阳光下的一个下午。

于 2011-04-28T18:26:34.873 回答
1

在我构建的数据仓库中,我使用了单独的 CALENDAR 和 TIME_OF_DAY 维度。第一个维度具有 1 天的粒度,第二个维度具有 1 分钟的粒度。

在另外两种情况下,我事先知道在小于 15 分钟的粒度上不需要报告。在那种情况下,为了简单起见,我使用了一个 CALENDAR 维度,每天有 96 条记录。

到目前为止,我在 Oracle 仓库中使用了这种方法,但今年夏天我可能会参与一个 PostgreSQL 仓库项目。

于 2011-05-09T15:33:44.247 回答