web-dev-qa-db-fra.com

Clé étrangère POSTGRESQL référençant les clés primaires de deux tables différentes

J'ai deux tableaux Livres et Livres audio, qui ont tous deux ISBN comme clé primaire. J'ai une table écrite par qui a un attribut isbn qui a une contrainte de clé étrangère pour les livres et livres audio ISBN. Le problème qui se pose lorsque j'insère dans écrit par est que postgresql veut que l'ISBN que j'insère dans écrit par soit dans les livres et les livres audio. Il est logique pour moi d'avoir une table écrite par qui stocke les auteurs et les livres/livres audio qu'ils ont écrits, mais cela ne se traduit pas par une table dans postgresql. La solution alternative que je pense mettre en œuvre était d'avoir deux nouvelles relations audiobook_writtenby et books_writtenby mais je ne suis pas sûr que ce soit une bonne alternative. Pourriez-vous me donner une idée de la façon dont je mettrais en œuvre mon idée originale d'avoir une seule table écrite en référençant deux tables différentes ou comment je pourrais mieux concevoir ma base de données. Faites-moi savoir si vous avez besoin de plus d'informations.

28
Jason Zhu

Il y a plus d'une façon de faire cela dans PostgreSQL. Personnellement, je préfère cette façon.

-- 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)
);

Vous pouvez utiliser l'héritage de table pour obtenir le meilleur des deux mondes. Créez les livres audio_writtenby et books_writtenby avec une clause INHERITS faisant référence à la table écrite par. Les clés étrangères peuvent être définies au niveau enfant comme vous le décrivez, mais vous pouvez toujours référencer des données au niveau supérieur. (Vous pouvez également le faire avec une vue, mais il semble que l'héritage pourrait être plus propre dans ce cas.)

Voir la documentation:

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

Notez que vous souhaiterez probablement ajouter un déclencheur BEFORE INSERT sur la table écrite par si vous faites cela.

7
kgrittn

Les SGBDR ne prennent pas en charge les contraintes de clé étrangère polymorphes. Ce que vous voulez faire est raisonnable, mais ce n'est pas quelque chose qui est bien accueilli par le modèle relationnel et l'un des vrais problèmes de non-correspondance d'impédance relationnelle d'objet lors de la création de systèmes ORM. Belle discussion à ce sujet sur Ward's WIki

Une approche à votre problème pourrait être de créer une table séparée, known_isbns, et de configurer des contraintes et/ou des déclencheurs sur les livres et les livres audio afin que cette table contienne tous les isbns valides des deux tables de livres spécifiques de type. Ensuite, votre contrainte FK sur écrit par vérifiera par rapport à known_isbns.

6
dbenhur