问题描述
我正在使用数据库表中的二进制检查is_deleted
字段对数据进行软删除。我在 country (父级)和 city (子级)表中db。问题是,如果用户尝试软删除在城市表中具有关联数据的国家/地区,则不应允许用户将其删除
个字段:id,name,is_deleted
城市表中的字段:id,is,is_deleted,country_id
(外键)
创建示例数据:
insert into country(id,is_deleted) values(1,'United Stated',false)
insert into city(id,country_id) values(1,'Washington',false,1)
尝试从具有关联数据的国家/地区软删除
update country set is_deleted = true where id = 1
解决方法
我能想到的唯一方法是有点尴尬:
/* add a column that mirrors "is_deleted" from "cuntry" */
ALTER TABLE city ADD country_is_deleted boolean DEFAULT FALSE NOT NULL;
/* this will be the target of the foreign key,no other purpose */
ALTER TABLE country ADD UNIQUE (id,is_deleted);
/* make sure "country_id" and "country_is_deleted" match the values
in "country",and that every soft delete in "country" is replicated */
ALTER TABLE city ADD FOREIGN KEY (country_id,country_is_deleted)
REFERENCES country (id,is_deleted)
ON UPDATE CASCADE;
/* make sure no country can be deleted when the city is not deleted */
ALTER TABLE city ADD CHECK (country_is_deleted IS FALSE OR is_deleted IS TRUE);
,
@LaurenzAlbe提供了一个非常酷的解决方案,但可能有点尴尬。也许触发器会少一些(尽管我通常不喜欢触发器)。无论哪种方式,您都需要考虑以下几点:当我为已标记为已删除的国家/地区插入城市,而新的城市未标记为已删除时,会发生什么情况?参见fiddle。
create or replace
function validate_all_cities_deleted()
returns trigger
language plpgsql
as $$
declare nl constant varchar(1) := chr(10);
begin
if exists
( select null
from city
where country_id = new.id
and not is_deleted
)
then
raise exception 'Business Rule Violation:% Cannot "delete" % while it has at least 1 non-deleted city.',nl,new.name
using
hint = format ('Check all cities with with country_id = %s (%s),set their is_deleted = True,then retry',new.id,new.name)
;
end if;
return new;
end;
$$;
create trigger remove_country_city_bur
before update of is_deleted
on country
for each row
execute function validate_all_cities_deleted();