使用筛选行比较排除约束

exclude constraint with filtered row comparison

示例基于https://www.postgresql.org/docs/current/btree-gist.html

假设我有一个架构:

CREATE TABLE zoo (
  cage   INTEGER,
  animal TEXT,
  is_agressive BOOLEAN,
  constraint no_different_animals_in_same_cage EXCLUDE USING gist (cage WITH =, animal WITH <>)
);

no_different_animals_in_same_cage 防止两只不同的动物在同一个笼子里。我想要的是仅当其中一只动物 is_aggressive IS TRUE 时才具有该约束。所以在 cage 中可以有一只带斑马的鹿,只要 none 的 is_aggressive 标志设置为 TRUE.

我该怎么做?

如果您只是想防止攻击性和非攻击性动物生活在同一个笼子里,您可能想将 is_aggressive 添加到 EXCLUDE 约束并将其转换为受支持的类型通过 btree_gist,例如int4(1 = 真,0 = 假):

 CREATE TABLE zoo (
  cage   INTEGER,
  animal TEXT,
  is_aggressive BOOLEAN,
  CONSTRAINT no_different_animals_in_same_cage 
    EXCLUDE USING gist (cage WITH =, animal WITH <>, int4(is_aggressive) WITH <>)
);

这会起作用:

INSERT INTO zoo VALUES(1,'zebra',false);
INSERT INTO zoo VALUES(1,'zebra',false);
INSERT INTO zoo VALUES(1,'deer',false);

INSERT INTO zoo VALUES(2,'lion',true);
INSERT INTO zoo VALUES(2,'lion',true);

这会失败:

INSERT INTO zoo VALUES(1,'lion',true);

ERROR:  conflicting key value violates exclusion constraint "no_different_animals_in_same_cage"
DETAIL:  Key (cage, animal, int4(is_aggressive))=(1, lion, 1) conflicts with existing key (cage, animal, int4(is_aggressive))=(1, zebra, 0).
SQL state: 23P01

请注意,仍然可以将两只具有攻击性的动物放入同一个笼子!为了防止它,您可以将 UNIQUE INDEX 添加到 table,这样在 EXCLUDE 约束之上它会检查传入的记录,看看是否有攻击性动物已经生活在特定的环境中笼子:

CREATE UNIQUE INDEX aggressive_animals_live_alone
ON zoo (cage,is_aggressive) WHERE (is_aggressive);

现在这也会失败:

INSERT INTO zoo VALUES(2,'lion',true);
INSERT INTO zoo VALUES(2,'killer bunny',true);

ERROR:  duplicate key value violates unique constraint "aggressive_animals_live_alone"
DETAIL:  Key (cage, is_aggressive)=(2, t) already exists.

如果你想执行更复杂的检查,我建议你看看触发器。

演示:db<>fiddle

正如@Arkaduisz Noster 在评论中指出的那样,如果允许至少一只攻击性动物与非攻击性动物共用一个笼子,则可以在 [=24] 中添加一个 WHERE 子句=]:

CREATE TABLE zoo (
  cage   INTEGER,
  animal TEXT,
  is_aggressive BOOLEAN,
  CONSTRAINT no_different_animals_in_same_cage 
    EXCLUDE USING gist (cage WITH =, animal WITH <>) WHERE (is_aggressive)
);

演示:db<>fiddle