Re: Composite UNIQUE across two tables? - Mailing list pgsql-sql

From Dirk Jagdmann
Subject Re: Composite UNIQUE across two tables?
Date
Msg-id 5d0f60990803111203m18345c5ata6f4d407867ed60d@mail.gmail.com
Whole thread Raw
In response to Re: Composite UNIQUE across two tables?  ("Jamie Tufnell" <diesql@googlemail.com>)
Responses Re: Composite UNIQUE across two tables?
Re: Composite UNIQUE across two tables?
List pgsql-sql
Hello Jamie,

I vote against duplicating site_group_id in the users table and the
proposed unique constraint with a function. Because all those might
fail, if you ever want to change the relationship between a site and a
site group.

My advise would be to have two triggers for insert/update on the site
and users table that check the uniqueness of the username with the
site_group. A have made some tests with inserts and updates on the
existing users and sites and these two functions seem to work.

One remark about your schema: If you use PostgreSQL, use the "text"
datatype for strings, since you don't limit yourself with the string
length. For my tests, I have modified your posted schema a bit, to
unify all column names to "name". You should set up an extensive
testcase if you haven't done already which should check every
combination of insert, update and delete on the three tables and see
if those are supposed to work, or should be restricted because of your
uniqueness constraints.

CREATE TABLE site_groups (  id serial primary key,  name text not null
);

CREATE TABLE sites (  id serial primary key,  site_group_id integer not null references site_groups,  name text not
null
);

CREATE TABLE users (  id serial,  site_id integer not null references sites,  name text not null
);

create or replace function user_check_unique_site_group()
returns trigger
as $$
declare c int; sg int;
begin -- get site_group id from site select into sg site_group_id from sites where id = NEW.site_id;
 -- check if we find any equal user names in the site group select into c count(*) from users, sites where
users.site_id= sites.id   and sites.site_group_id = sg   and users.name = NEW.name;
 
 -- nothing found, this user name is ok if c = 0 then   return NEW; end if;
 raise exception 'username is not unique with site group'; return NULL;
end;
$$ language plpgsql;

create trigger user_check_unique_site_group
before update or insert
on users
for each row
execute procedure user_check_unique_site_group();

create or replace function sites_check_unique_username()
returns trigger
as $$
declare c int;
begin -- if the site group is unmodified we're safe if NEW.site_group_id = OLD.site_group_id then   return NEW; end
if;
 -- check if the same username is in the old and new site group select into c count(*) from users, sites where
users.site_id= sites.id   and sites.site_group_id = NEW.site_group_id   and users.name in (                      select
users.name                     from users, sites                      where users.site_id = sites.id
   and sites.site_group_id = OLD.site_group_id                     );
 
 -- nothing found, we're safe if c = 0 then   return NEW; end if;
 raise exception 'username is not unique with site group'; return NULL;
end;
$$ language plpgsql;

create trigger sites_check_unique_username
before update
on sites
for each row
execute procedure sites_check_unique_username();

-- 
---> Dirk Jagdmann
----> http://cubic.org/~doj
-----> http://llg.cubic.org


pgsql-sql by date:

Previous
From: Steve Midgley
Date:
Subject: Re: works but ...
Next
From: "Rodrigo E. De León Plicet"
Date:
Subject: Re: Composite UNIQUE across two tables?