【问题标题】:Preventing adjacent/overlapping entries with EXCLUDE in PostgreSQL在 PostgreSQL 中使用 EXCLUDE 防止相邻/重叠条目
【发布时间】:2013-10-21 21:06:19
【问题描述】:

我正在创建一个在 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,并且我将数据库的时区设置为“ UTC”在 postgresql.conf 中)。

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

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

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

【问题讨论】:

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


    【解决方案1】:

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

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

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

    为了保持干净,我会使用range functions 对所有带有CHECK constraint 的条目强制执行[) 边界(包括下限和不包括上限):

    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)。

    【讨论】:

    • 这个解决方案非常有效。虽然我可以使用单独的列来伪造它,但切换到 tsrange 使这变得微不足道, 为我提供了所有其他范围运算符,它们对于使用/管理这些数据将非常方便.谢谢! :)
    • 如果我想在插入相邻的 tsr 值时使用 ON CONFLICT 子句,这会有用吗?我想知道如果插入相邻的 tsr 值是否会触发 ON CONFLICT(tsr)。
    • @madtyn:是的,ON CONFLICT 子句涵盖了与EXCLUSION 约束的冲突,但适用于DO NOTHINGThe manual: Note that exclusion constraints are not supported as arbiters with ON CONFLICT DO UPDATE. 考虑在我上面添加的更新的数据库小提琴中的演示。
    【解决方案2】:

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

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

    【讨论】:

    • 问题是,除了非重叠约束之外,我还想阻止 adjacent 条目。如果我们让unit 成为这些类型将存储的最小时间单位,对于任何两个条目fromA - toAfromB - toB,如果toA + unit = fromB(反之亦然),那么它们可以表示为单个条目fromA - toB。我遇到的问题是我不知道unit 是什么,或者它是否已定义,所以我目前使用的值是1 (second)
    • 单位是微秒的一小部分,除非您使用时间戳(0)将其强制为一秒,偶尔会出现舍入问题,从而使您遇到排除约束。对于相邻的约束,我敢打赌你可以排除一个表达式,例如((during + interval '1 second') with &&).
    【解决方案3】:

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

    你没问题,考虑一下:

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

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2020-05-09
      • 2019-04-11
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多