我可以使用模式约束来强制执行唯一性吗?

问题描述 投票:0回答:1

假设我有两个相关实体,例如一个项目和一个过滤器。过滤器可以是:

  • 共享,即适用于所有项目
  • 私有,即它仅限于一个项目

每个过滤器都有一个名称,我想确保过滤器具有唯一的名称。但是,唯一性取决于过滤器是共享的还是私有的。如果过滤器是共享的,则其名称在所有共享过滤器中必须唯一[,任何私有过滤器都不能重复使用该名称。如果过滤器是私有的,则它必须是唯一的[[在其项目内,即。两个不同的项目可以有两个具有相同名称的专用过滤器。

我将过滤器数据存储在数据库表中,我想添加一个约束以强制执行这些命名规则。是否可以构造一个复合密钥来做到这一点?我正在想象过滤器表上的三列:

过滤器的名称(字符串)

    一个共享/私有标志(布尔)
  • 项目ID(数字,最好是项目表的外键)
  • [共享过滤器将具有共享为true的名称,没有项目ID,而私有过滤器将具有共享共享为false的名称及其所属项目的ID。
  • 我可以构造一个约束来强制执行此操作吗?

    对我的方法有任何想法吗?我是在正确的轨道上还是有更好的方法?

  • sql constraints
    1个回答
    0
    投票
    我能够设计这张桌子:

    create table filters ( filter_id int auto_increment primary key, filter_name varchar(20) not null, project_id int, is_shared bool as (case when project_id is null then true else null end), unique key (filter_name, project_id), unique key (filter_name, is_shared) );

    这将强制执行以下约束:共享过滤器必须是唯一的,并且私有过滤器在同一项目的其他过滤器中必须是唯一的。

    即以下过滤器成功:

    insert into filters set filter_name = 'shared1', project_id = null; -- OK insert into filters set filter_name = 'shared2', project_id = null; -- OK insert into filters set filter_name = 'private1', project_id = 1; -- OK insert into filters set filter_name = 'private2', project_id = 1; -- OK insert into filters set filter_name = 'private1', project_id = 2; -- OK insert into filters set filter_name = 'private2', project_id = 2; -- OK

    但是这些失败,正确的是:

    insert into filters set filter_name = 'shared1', project_id = null; -- DUPLICATE
    insert into filters set filter_name = 'private1', project_id = 1;   -- DUPLICATE
    insert into filters set filter_name = 'private1', project_id = 2;   -- DUPLICATE
    

    但是,我无法解决的棘手部分是使私有过滤器名称与共享过滤器名称冲突,但与来自不同项目的私有过滤器名称不冲突。

    insert into filters set filter_name = 'shared1', project_id = 1;
    insert into filters set filter_name = 'private1', project_id = null;
    

    我们不能仅仅使filter_name为唯一,因为这会使来自不同项目的私有过滤器相互冲突。

    unique key (filter_name) -- WRONG
    

    我也研究过创建VIEW WITH CHECK OPTION,但是这将需要使用自联接或NOT EXISTS子查询,并且这两种情况都意味着该视图不支持WITH CHECK OPTION。

    我能够通过MySQL 5.7中的触发器来做到这一点:

    delimiter // create trigger it1 before insert on filters for each row begin if exists (select * from filters as f2 where f2.filter_name = NEW.filter_name and f2.project_id is null) then signal sqlstate '45000' set message_text = 'filter conflicts with an existing shared filter'; end if; if exists (select * from filters as f2 where f2.filter_name = NEW.filter_name and NEW.project_id is null) then signal sqlstate '45000' set message_text = 'filter conflicts with an existing private filter'; end if; if exists (select * from filters as f2 where f2.filter_name = NEW.filter_name and f2.project_id = NEW.project_id) then signal sqlstate '45000' set message_text = 'filter conflicts with an existing private filter in the same project'; end if; end// delimiter ;

    © www.soinside.com 2019 - 2024. All rights reserved.