Re: Foreign Key normalization question - Mailing list pgsql-general

From D. Dante Lorenso
Subject Re: Foreign Key normalization question
Date
Msg-id 48BDB19D.4020101@lorenso.com
Whole thread Raw
In response to Re: Foreign Key normalization question  (Matthew Wilson <matt@tplus1.com>)
List pgsql-general
Matthew Wilson wrote:
> On Tue 02 Sep 2008 04:40:55 PM EDT, Scott Marlowe wrote:
>> On Tue, Sep 2, 2008 at 2:35 PM, Matthew Wilson <matt@tplus1.com> wrote:
>>> On Tue 02 Sep 2008 04:19:41 PM EDT, Scott Marlowe wrote:
>>>> If the two subordinate tables ALWAYS have to point to the same place,
>>>> why two tables?  Can't a customer have > 1 location?  I'm pretty sure
>>>> IBM has more than one corporate office you could ship things to.
>>> Yeah, so the idea is one customer might have many locations and many
>>> products.  And at each location, some subset of all their products is
>>> available.
>> You could have the product_locations have a custid1 and custid2 fields
>> that reference the two parent tables, and then a check constraing on
>> product_locations that custid1=custid2
>
> You inspired me to change my tables to this:
>
> create table location (
>     id serial unique,
>     name text,
>     customer_id int references customer,
>     primary key (id, customer_id)
> );
>
> create table product (
>     id serial unique,
>     name text,
>     customer_id int references customer,
>     primary key (id, customer_id)
> );
>
> create table product_location (
>     product_id int references product (id),
>     product_customer_id int references customer (id),
>     location_id int references location (id),
>     location_customer_id int references customer (id) check product_customer_id = location_customer_id,
>     foreign key (product_id, product_customer_id) references product (id, customer_id),
>     foreign key (location_id, location_customer_id) references location (id, customer_id),
> );
>
> This seems to work based on my informal testing, but it seems really
> byzantine.  I wish I didn't have to explicitly put the customer IDs in
> the table.
>
> Is there a better way?

You could add a trigger to your product_location table that just
double-checked the customers matched or prevents the insert/update.  A
PL/PGSQL function like this might help:

---------- 8< -------------------- 8< ----------

DECLARE
   is_ok BOOLEAN;
BEGIN
   SELECT p.customer_id = l.customer_id
   INTO is_ok
   FROM product p, location l
   WHERE p.product_id = NEW.product_id
   AND l.location_id = NEW.location_id;

   -- didnt find the product and location ... weird
   IF NOT FOUND THEN
       RETURN NULL;
   END;

   -- product customer matches the location customer
   IF is_ok = TRUE THEN
       RETURN NEW;
   END;

   -- product and location customers did NOT match, reject changes
   RETURN NULL;
END;
---------- 8< -------------------- 8< ----------

Disclaimer: I have no idea if that code works.  I just whipped it up now
without testing it.  That might do your checks without having to add
columns to tables you don't want to add.

Good luck.

-- Dante

pgsql-general by date:

Previous
From: "Roberts, Jon"
Date:
Subject: Re: plpgsql returning resultset
Next
From: Alex Vinogradovs
Date:
Subject: Re: plpgsql returning resultset