使用PostgreSQL中的EXCLUDE防止相邻/重叠的条目

时间:2013-10-21 21:06:19

标签: sql postgresql datetime range-types exclusion-constraint

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

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

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的分辨率都高于一秒。

我的问题是:是否有任何问题,只是假设第二个解决方案,因为这是我的所有应用程序需要(或想要),或者,如果有,我怎么能改变这个约束来处理分数第二个是强有力的?

3 个答案:

答案 0 :(得分:17)

范围类型consist of a lower and an upper border, which can be included or excluded。 典型的用例(范围类型的默认值)是包含下限和排除上限。

排除重叠范围似乎很清楚。有一个nice code example in the manual

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

为了保持清洁,我会使用CHECK constraintrange functions的所有条目强制执行[)边界(包括下限和排除上限):

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<>小提琴here
(旧SQL Fiddle

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

答案 1 :(得分:1)

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

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

答案 2 :(得分:0)

  

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

你没关系,请考虑:

select 
  extract ('epoch' from now())
  , extract ('epoch' from now()::timestamp(0))