ruby-on-rails 通过关联验证来自不同表的2列之间的唯一性

ncecgwcz  于 4个月前  发布在  Ruby
关注(0)|答案(1)|浏览(102)

我有一个设计问题,在rails应用程序中处理postgres数据库上的唯一性约束。
我有:

  • companies表,其中包含firm_idname
  • 一个firms表,包含一个group_id列(id链接到companies.firm_id
  • groups表(id链接到firms.group_id

我想确保company.name + company.firm.group_id的唯一性,所以在两个表中有某种多列唯一索引。
这可能吗
我不知道/认为我们如何/是否可以在本地使用postgres索引,所以我看到的解决方案是:

  • companies表上复制冗余的group_id列,这会导致这2列之间的数据同步/可靠性维护问题。这就是我现在所做的,它并不理想。
  • 不使用数据库索引,只依赖rails验证,这可能是查询繁重的,并且在大规模更新时会遇到竞争条件

我不是特别喜欢这些解决方案,有人想到我们可以做些什么吗?

e1xvtsh3

e1xvtsh31#

常规的constraint不能跨多个表,但是您可以使用constraint trigger来强制执行完全任意的约束逻辑。

create table groups(   
    id serial primary key );
create table firms(    
    id serial primary key,
    group_id int references groups(id) );
create table companies(
    id serial primary key,
    firm_id int references firms(id),
    name text );

x

create function f_unique_company_name_per_group()returns trigger as $f$
declare current_company_group int:=(select group_id 
                                    from firms 
                                    where id=new.firm_id);
begin 
    if exists(select from companies inner join firms
             on  companies.firm_id=firms.id
             and companies.name=new.name
             and companies.id is distinct from new.id
             and firms.group_id=current_company_group) then
        raise exception using 
            detail=format('Company name %L already exists in group %L',
                               new.name,
                               current_company_group),
            errcode = 23505, --'unique_violation',
            column='name',
            table=TG_TABLE_NAME,
            schema=TG_TABLE_SCHEMA,
            datatype='text',
            constraint='t_unique_company_name_per_group';
    end if;
    return new;
end $f$ language plpgsql;

create constraint trigger t_unique_company_name_per_group 
after insert or update on companies
for each row execute function f_unique_company_name_per_group();
insert into groups values (1),(2);
insert into firms values (1,1),(2,1),(3,1);
insert into companies values (1,1,'company1'),(2,2,'company2');
--different company, different firm, but the same name within group:
insert into companies values (3,3,'company1');
ERROR:  23505
DETAIL:  Company name 'company1' already exists in group '1'
CONTEXT:  PL/pgSQL function f_unique_company_name_per_group() line 9 at RAISE

的数据

相关问题