12

我正在创建一个在 PostgreSQL 9.2.4 中存储任意日期/时间范围的数据库。我想在这个数据库上设置一个约束,强制日期/时间范围不重叠,也不相邻(因为两个相邻的范围可以表示为一个连续的范围)。

为此,我使用了EXCLUDE带有 GiST 索引的约束。这是我目前的约束:

ADD CONSTRAINT overlap_exclude EXCLUDE USING GIST (
    box(
        point (
            extract(EPOCH FROM "from") - 1,
            extract(EPOCH FROM "from") - 1
        ),
        point (
            extract(EPOCH FROM "to"),
            extract(EPOCH FROM "to")
        )
    ) WITH &&
);

fromto都是TIMESTAMP WITHOUT TIME ZONE, 并且是存储在 UTC 中的日期/时间(我在将数据插入应用程序中的这些列之前转换为 UTC,并且我在 postgresql.conf 中将数据库的时区设置为“UTC”)。

不过,我想我可能遇到的问题是,这个约束正在做出(不正确的)假设,即没有小于一秒的时间增量。

值得注意的是,对于我存储的特定数据,我只需要第二个分辨率。但是,我觉得我可能仍然需要处理这个问题,因为 SQL 类型timestamptimestamptz分辨率都高于一秒。

我的问题是:简单地假设第二个解决方案有什么问题,因为这就是我的应用程序需要(或想要的),或者,如果有,我如何改变这个约束来处理几分之一秒健壮的方式?

4

3 回答 3

26

范围类型由下边界和上边界组成,可以包括或排除。典型的用例(以及范围类型的默认值)是包含下限并排除上限。

排除重叠范围似乎很清楚。手册中有一个很好的代码示例

此外,创建另一个排除约束,使用相邻运算符-|-也排除相邻条目。两者都必须基于GiST索引,因为目前不支持 GIN。

为了保持干净,我将使用范围函数[)对所有具有CHECK约束的条目强制限制(包括下限和不包括上限) :

CREATE TABLE tbl (
   tbl_id serial PRIMARY KEY
 , tsr tsrange
 , CONSTRAINT tsr_no_overlap  EXCLUDE USING gist (tsr WITH &&)
 , CONSTRAINT tsr_no_adjacent EXCLUDE USING gist (tsr WITH -|-)
 , CONSTRAINT tsr_enforce_bounds CHECK (lower_inc(tsr) AND NOT upper_inc(tsr))
);

db<>fiddle here
(Old SQL Fiddle )

不幸的是,这会创建两个相同的 GiST 索引来实现两个排除约束,从逻辑上讲,一个就足够了。这似乎是当前实现的一个缺点(至少到 Postgres 11)。

于 2013-10-21T22:23:17.713 回答
1

您可以使用 9.2 中引入的范围类型重写排除。更好的是,您可以用一个范围替换这两个字段。请参阅此处的“范围约束”,其中的示例基本上相当于您的用例:

http://www.postgresql.org/docs/current/static/rangetypes.html

于 2013-10-21T21:27:39.210 回答
0

不过,我想我可能遇到的问题是,这个约束正在做出(不正确的)假设,即没有小于一秒的时间增量。

你没问题,考虑:

select 
  extract ('epoch' from now())
  , extract ('epoch' from now()::timestamp(0))
于 2013-12-13T10:56:41.213 回答