Re: Foreign Key normalization question

From: "D(dot) Dante Lorenso" <dante(at)lorenso(dot)com>
To: Matthew Wilson <matt(at)tplus1(dot)com>
Cc: pgsql-general(at)postgresql(dot)org
Subject: Re: Foreign Key normalization question
Date: 2008-09-02 21:35:25
Message-ID: 48BDB19D.4020101@lorenso.com
Views: Raw Message | Whole Thread | Download mbox | Resend email
Thread:
Lists: 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(at)tplus1(dot)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

In response to

Responses

Browse pgsql-general by date

  From Date Subject
Next Message Richard Broersma 2008-09-02 22:30:58 Subqueries in Check() -- Still Intentionally Omitted?
Previous Message Tom Lane 2008-09-02 21:30:52 Re: How to create a case-insensitive unique constraint?