如何添加部分约束并且还适用于 postgres 的重叠日期范围?
How to add a constraint that's partial and also works with overlapping daterange for postgres?
我在 thing_thing
table.
中使用带有名为 valid_period
的日期范围字段的 postgres
我用来添加约束的查询
CREATE EXTENSION IF NOT EXISTS btree_gist;
ALTER TABLE thing_thing
ADD CONSTRAINT prevent_overlapping_valid_periods_by_team
EXCLUDE USING gist(team_id WITH =, valid_period WITH &&)
DEFERRABLE INITIALLY DEFERRED;
这个效果很好。
- 具有相同
team_id
和重叠 valid_period
的行 => 已屏蔽
- 行有不同
team_id
和重叠 valid_period
=> 允许 ✅
- 具有相同
team_id
且不重叠的行 valid_period
=> 允许 ✅
我需要什么但不知道该怎么做
但我只希望约束在 IF
中生效
- 字段
is_removed
(布尔字段)也为假或
- 字段
state
(varchar 字段)包含任何可能的值之一,例如 CANCELLED
、REJECTED
因此以下内容不起作用:
- 具有相同
team_id
和重叠 valid_period
的行,但现有行 is_removed
为真 => 预期允许 ✅ 但被阻止
- 具有相同
team_id
和重叠 valid_period
的行,但现有行 state
是 CANCELLED
=> 预期允许 ✅ 但被阻止
- 具有相同
team_id
和重叠 valid_period
的行,但现有行 state
是 REJECTED
=> 预期允许 ✅ 但被阻止
如何更改约束以允许例外?也就是说,有点像partial unique index.
我有点悲观,因为我知道没有来自另一个 SO 答案的部分约束 here
但同时,我认为不可能使用部分唯一索引来防止非法重叠日期范围。
您可以为此使用部分索引 - 至少您可以使用 PostgreSQL v13 - 尚未检查其他索引。
请注意 WHERE
条件中的括号 - 如果您忘记这些括号,它会提出抗议。
下面的示例根据您的描述进行了稍微简化,但逻辑上是相同的。
=> CREATE TABLE teams (team_id int not null, valid_period daterange not null, is_removed boolean not null, state char not null);
=> ALTER TABLE teams ADD CONSTRAINT no_overlaps
EXCLUDE USING gist(team_id WITH =, valid_period WITH &&)
WHERE (is_removed = false OR state IN ('C','R'));
=> INSERT INTO teams VALUES (1, daterange('2020-01-01', '2021-01-01'), false, 'X');
INSERT 0 1
=> INSERT INTO teams VALUES (1, daterange('2020-01-01', '2021-01-01'), false, 'X');
ERROR: conflicting key value violates exclusion constraint "no_overlaps"
DETAIL: Key (team_id, valid_period)=(1, [2020-01-01,2021-01-01)) conflicts with existing key (team_id, valid_period)=(1, [2020-01-01,2021-01-01)).
=> INSERT INTO teams VALUES (1, daterange('2020-01-01', '2021-01-01'), true, 'X');
INSERT 0 1
=> INSERT INTO teams VALUES (1, daterange('2020-01-01', '2021-01-01'), true, 'C');
ERROR: conflicting key value violates exclusion constraint "no_overlaps"
DETAIL: Key (team_id, valid_period)=(1, [2020-01-01,2021-01-01)) conflicts with existing key (team_id, valid_period)=(1, [2020-01-01,2021-01-01)).
我在 thing_thing
table.
valid_period
的日期范围字段的 postgres
我用来添加约束的查询
CREATE EXTENSION IF NOT EXISTS btree_gist;
ALTER TABLE thing_thing
ADD CONSTRAINT prevent_overlapping_valid_periods_by_team
EXCLUDE USING gist(team_id WITH =, valid_period WITH &&)
DEFERRABLE INITIALLY DEFERRED;
这个效果很好。
- 具有相同
team_id
和重叠valid_period
的行 => 已屏蔽 - 行有不同
team_id
和重叠valid_period
=> 允许 ✅ - 具有相同
team_id
且不重叠的行valid_period
=> 允许 ✅
我需要什么但不知道该怎么做
但我只希望约束在 IF
中生效- 字段
is_removed
(布尔字段)也为假或 - 字段
state
(varchar 字段)包含任何可能的值之一,例如CANCELLED
、REJECTED
因此以下内容不起作用:
- 具有相同
team_id
和重叠valid_period
的行,但现有行is_removed
为真 => 预期允许 ✅ 但被阻止 - 具有相同
team_id
和重叠valid_period
的行,但现有行state
是CANCELLED
=> 预期允许 ✅ 但被阻止 - 具有相同
team_id
和重叠valid_period
的行,但现有行state
是REJECTED
=> 预期允许 ✅ 但被阻止
如何更改约束以允许例外?也就是说,有点像partial unique index.
我有点悲观,因为我知道没有来自另一个 SO 答案的部分约束 here
但同时,我认为不可能使用部分唯一索引来防止非法重叠日期范围。
您可以为此使用部分索引 - 至少您可以使用 PostgreSQL v13 - 尚未检查其他索引。
请注意 WHERE
条件中的括号 - 如果您忘记这些括号,它会提出抗议。
下面的示例根据您的描述进行了稍微简化,但逻辑上是相同的。
=> CREATE TABLE teams (team_id int not null, valid_period daterange not null, is_removed boolean not null, state char not null);
=> ALTER TABLE teams ADD CONSTRAINT no_overlaps
EXCLUDE USING gist(team_id WITH =, valid_period WITH &&)
WHERE (is_removed = false OR state IN ('C','R'));
=> INSERT INTO teams VALUES (1, daterange('2020-01-01', '2021-01-01'), false, 'X');
INSERT 0 1
=> INSERT INTO teams VALUES (1, daterange('2020-01-01', '2021-01-01'), false, 'X');
ERROR: conflicting key value violates exclusion constraint "no_overlaps"
DETAIL: Key (team_id, valid_period)=(1, [2020-01-01,2021-01-01)) conflicts with existing key (team_id, valid_period)=(1, [2020-01-01,2021-01-01)).
=> INSERT INTO teams VALUES (1, daterange('2020-01-01', '2021-01-01'), true, 'X');
INSERT 0 1
=> INSERT INTO teams VALUES (1, daterange('2020-01-01', '2021-01-01'), true, 'C');
ERROR: conflicting key value violates exclusion constraint "no_overlaps"
DETAIL: Key (team_id, valid_period)=(1, [2020-01-01,2021-01-01)) conflicts with existing key (team_id, valid_period)=(1, [2020-01-01,2021-01-01)).