在PostgreSQL中为自引用关联表添加唯一约束

qltillow  于 2023-06-22  发布在  PostgreSQL
关注(0)|答案(1)|浏览(159)

SQLFiddle
我有一个成分表和一个将成分相互链接的自引用关联表。
我想添加一个约束,确保两种成分之间的每个关联只能存在一次。如果“salt”已经与“pepper”相关联,我想阻止关联表中的另一行将“pepper”与“salt”相关联。
我知道我可以使用触发器,但因为它更复杂,我更喜欢使用约束。
(使用PostgreSQL 14+)

  • a:使用约束条件是否可能?
  • B:如果不是,触发器是最直接的解决方案还是有更好的方法?
cygmwpex

cygmwpex1#

一个选项在列元组的最大/最小值上使用唯一索引:

create unique index ingredient_to_ingredient_idx 
on ingredient_to_ingredient( 
    greatest(ingredient_a_id, ingredient_b_id),
    least(ingredient_a_id, ingredient_b_id)
);

Demo on DB Fiddle

-- I want this to produce a constraint error
insert into ingredient_to_ingredient (ingredient_a_id, ingredient_b_id) values (2, 1); 

-- ERROR:  duplicate key value violates unique constraint "ingredient_to_ingredient_idx"
-- DETAIL:  Key (GREATEST(ingredient_a_id, ingredient_b_id), LEAST(ingredient_a_id, ingredient_b_id))=(2, 1) already exists.

另一种方法是排除约束:

alter table ingredient_to_ingredient
add constraint ingredient_to_ingredient_excl exclude using gist(
        least(ingredient_a_id, ingredient_b_id)    with =,
        greatest(ingredient_a_id, ingredient_b_id) with =
);

这将为错误的insert语句产生以下错误:

-- ERROR:  conflicting key value violates exclusion constraint "ingredient_to_ingredient_excl"
-- DETAIL:  Key (LEAST(ingredient_a_id, ingredient_b_id), GREATEST(ingredient_a_id, ingredient_b_id))=(1, 2) conflicts with existing key (LEAST(ingredient_a_id, ingredient_b_id), GREATEST(ingredient_a_id, ingredient_b_id))=(1, 2).

Fiddle

相关问题