How to ensure entries with non-overlapping time ranges?

橙三吉。 提交于 2021-02-08 05:16:26

问题


I need to ensure my database only contains entries where two or more of its columns are unique. This can easily be achieved with a UNIQUE constraint over those columns.

In my case, I need to forbid duplication only for overlapping time ranges. The table has valid_from and valid_to columns. In some cases one might first need to expire the active entry to by setting valid_to = now, and then inserting a new entry adjusted to valid_from = now and valid_to = infinity.

I seem to be able to expire the prior entry without any problems using UPDATE, but inserting the new entry seems to be troublesome since my base columns are currently UNIQUE, and therefore can't be added again.

I thought of adding valid_from and valid_to as part of the UNIQUE constraint, but that would just make the constraint more loose, and allow duplicates and overlapping time ranges to exist.

How do I make a constraint to ensure that duplicates don't exist with overlapping valid_from and valid_to tsrange?

I seem to be looking for EXCLUDE USING GIST, but it does not seem to support multiple columns? This does not seem to work for me:

ALTER TABLE registration 
DROP Constraint IF EXISTS registration_{string.Join('_', listOfAttributes)}_key, 
ADD Constraint registration_{string.Join('_', listOfAttributes)}_key EXCLUDE USING GIST({string.Join(',', listOfAttributes)} WITH =, valid WITH &&);

回答1:


You were on the right track. But the syntax for exclusion constraints is slightly different.

Depending on the undisclosed table definition, you may need to install the extension (additional module) btree_gist first. Once per db. It's needed for my example since the required operator class is not installed for type integer by default:

CREATE EXTENSION btree_gist;

See:

  • PostgreSQL EXCLUDE USING error: Data type integer has no default operator class
  • How to use (install) dblink in PostgreSQL?

Then:

CREATE TABLE registration  (
  tbl_id integer PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY
, col_a  integer NOT NULL
, col_b  integer NOT NULL
, valid_from timestamp
, valid_to   timestamp
, CONSTRAINT no_overlap
    EXCLUDE USING gist (col_a with =, col_b with =, tsrange(valid_from, valid_to) WITH &&)
);

Each column needs to be listed with its respective operator.

And you need a range type. You mention separate columns valid_from and valid_to. And you also mention tsrange and valid in the failed command. That's confusing. Assuming two timestamp columns, an expression index with the expression tsrange(valid_from, valid_to) would do it.

Related:

  • Perform this hours of operation query in PostgreSQL
  • Non-overlap, continuous timestamp ranges (tstzrange) for opening hours
  • Postgresql 9.4 query gets progressively slower when joining TSTZRANGE with &&
  • Store the day of the week and time?

Typically, timestamptz (tstzrange) should be chosen over timestamp (tsrange). See:

  • Ignoring time zones altogether in Rails and PostgreSQL

Maybe, a superior design would be a one-to-many relationship between your registration table and 1-N entries in a new registration_range table. And some logic to determine the currently valid entry (for any given point in time). Depends on more undisclosed information.



来源:https://stackoverflow.com/questions/61716095/how-to-ensure-entries-with-non-overlapping-time-ranges

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