Preventing adjacent/overlapping entries with EXCLUDE in PostgreSQL

喜欢而已 提交于 2019-11-26 06:44:28

问题


I am creating a database which stores arbitrary date/time ranges in PostgreSQL 9.2.4. I want to place a constraint on this database which forces the date/time ranges to be non-overlapping, and non-adjacent (since two adjacent ranges can be expressed as a single continuous range).

To do this, I am using an EXCLUDE constraint with a GiST index. Here is the constraint I have currently:

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 &&
);

The columns from and to are both TIMESTAMP WITHOUT TIME ZONE, and are date/times stored in UTC (I convert to UTC before inserting data into these columns in my application, and I have my database\'s timezone set to \"UTC\" in postgresql.conf).

The problem I am thinking I might have, though, is that this constraint is making the (incorrect) assumption that there are no time increments smaller than one second.

It is worth noting that, for the particular data I am storing, I only need second resolution. However, I feel that I may still need to deal with this since the SQL types timestamp and timestamptz are both higher resolution than one second.

My question is either: is there any problem with simply assuming second resolution, since that\'s all my application needs (or wants), or, if there is, how can I alter this constraint to deal with fractions-of-a-second in a robust way?


回答1:


Range types consist of a lower and an upper border, which can be included or excluded. The typical use case (and default for range types) is to include the lower and exclude the upper bound.

Excluding overlapping ranges seems clear. There is a nice code example in the manual

In addition, create another exclusion constraint employing the adjacent operator -|- to also exclude adjacent entries. Both must be based on GiST indexes as GIN is currently not supported for this.

To keep it clean, I'd enforce [) bounds (including lower and excluding upper) for all entries with a CHECK constraint using range 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<>fiddle here
(Old SQL Fiddle)

Unfortunately, this creates two identical GiST indexes to implement both exclusion constraints, where one would suffice, logically. That seems to be a shortcoming of the current implementation (up to at least Postgres 11).




回答2:


You can rewrite the exclude with the range type introduced in 9.2. Better yet, you could replace the two fields with a range. See "Constraints on Ranges" here, with an example that basically amounts to your use case:

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




回答3:


The problem I am thinking I might have, though, is that this constraint is making the (incorrect) assumption that there are no time increments smaller than one second.

You're OK there, consider:

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


来源:https://stackoverflow.com/questions/19504727/preventing-adjacent-overlapping-entries-with-exclude-in-postgresql

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!