POSTGRESQL Foreign Key Referencing Primary Keys of two Different Tables
Solution 1:
There's more than one way to do this in PostgreSQL. Personally, I prefer this way.
-- This table should contain all the columns common to both
-- audio books and printed books.
create table books (
isbn char(13) primary key,
title varchar(100) not null,
book_type char(1) not null default 'p'
check(book_type in ('a', 'p')),
-- This unique constraint lets the tables books_printed and books_audio
-- target the isbn *and* the type in a foreign key constraint.
-- This prevents you from having an audio book in this table
-- linked to a printed book in another table.
unique (isbn, book_type)
);
-- Columns unique to printed books.
create table books_printed (
isbn char(13) primary key references books (isbn),
-- Allows only one value. This plus the FK constraint below guarantee
-- that this row will relate to a printed book row, not an audio book
-- row, in the table books. The table "books_audio" is similar.
book_type char(1) default 'p'
check (book_type = 'p'),
foreign key (isbn, book_type) references books (isbn, book_type),
other_columns_for_printed_books char(1) default '?'
);
-- Columns unique to audio books.
create table books_audio (
isbn char(13) primary key references books (isbn),
book_type char(1) default 'a'
check (book_type = 'a'),
foreign key (isbn, book_type) references books (isbn, book_type),
other_columns_for_audio_books char(1) default '?'
);
-- Authors are common to both audio and printed books, so the isbn here
-- references the table of books.
create table book_authors (
isbn char(13) not null references books (isbn),
author_id integer not null references authors (author_id), -- not shown
primary key (isbn, author_id)
);
Solution 2:
You could use table inheritance to kinda get the best of both worlds. Create the audiobook_writtenby and books_writtenby with an INHERITS
clause referencing the writtenby table. The foreign keys could be defined at the child level as you describe, but you could still reference data at the higher level. (You could also do this with a view, but it sounds like inheritance might be cleaner in this case.)
See the docs:
http://www.postgresql.org/docs/current/interactive/sql-createtable.html
http://www.postgresql.org/docs/current/interactive/tutorial-inheritance.html
http://www.postgresql.org/docs/current/interactive/ddl-inherit.html
Note that you will probably want to add a BEFORE INSERT trigger on the writtenby table if you do this.
Solution 3:
RDBMS's do not support polymorphic foreign key constraints. What you want to do is reasonable, but not something accommodated well by the relational model and one of the real problems of object relational impedance mismatch when making ORM systems. Nice discussion on this on Ward's WIki
One approach to your problem might be to make a separate table, known_isbns, and set up constraints and/or triggers on Books and AudioBooks so that table contains all the valid isbns of both type specific book tables. Then your FK constraint on writtenby will check against known_isbns.