2012-04-09 17 views
13

Tengo dos tablas libros y audiolibros, que tienen ISBN como claves principales. Tengo una tabla por escrito que tiene un atributo isbn que tiene una restricción de clave externa para Libros y Audiolibros ISBN. El problema que surge cuando inserto en el escrito es que postgresql quiere que el ISBN que inserto en escritoby esté en ambos libros y audiolibros. Tiene sentido para mí tener una tabla escrita por eso almacena autores y los libros/audiolibros que han escrito, sin embargo, esto no se traduce en una tabla en postgresql. La solución alternativa que estoy pensando implementar es tener dos nuevas relaciones, audiobook_writtenby y books_writtenby, pero no estoy seguro de que sea una buena alternativa. ¿Podría darme una idea de cómo implementaría mi idea original de tener una sola tabla escrita haciendo referencia a dos tablas diferentes o cómo podría diseñar mejor mi base de datos? Déjeme saber si usted necesita más información.POSTGRESQL Clave externa que hace referencia a las claves principales de dos tablas diferentes

Respuesta

4

RDBMS no admiten restricciones de clave externa polimórficas. Lo que quiere hacer es razonable, pero no algo acomodado por el modelo relacional y uno de los problemas reales de la desigualdad de impedancia relacional del objeto al hacer sistemas ORM. Nice discussion on this on Ward's WIki

Un enfoque para su problema podría ser crear una tabla separada, información conocida, y configurar restricciones y/o desencadenantes en libros y audiolibros para que la tabla contenga todos los íconos válidos de ambas tablas de libros específicos. Entonces su restricción de FK en escritoby se comparará con known_isbns.

+3

El modelo relacional y las bases de datos SQL realmente manejan bien este tipo de cosas. El problema no es relacional o SQL; el problema es que una de las restricciones obvias se implementa incorrectamente. (La restricción es que los ISBN para libros y audiolibros proceden del mismo dominio.) –

3

Puede usar la herencia de tablas para obtener lo mejor de ambos mundos. Crea el audiobook_writtenby y books_writtenby con una cláusula INHERITS que hace referencia a la tabla writtenby. Las claves foráneas podrían definirse en el nivel secundario como usted describe, pero aún podría hacer referencia a los datos en el nivel superior. (También puede hacer esto con una vista, pero suena como herencia podría ser más limpio en este caso.)

Consulte los documentos:

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

Tenga en cuenta que es probable que desee agregar un desencadenante ANTES DE INSERTAR en la tabla writeby si hace esto.

26

Hay más de una forma de hacer esto en PostgreSQL. Personalmente, prefiero de esta manera.

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

En este ejemplo específico, no es necesario utilizar varias tablas. Simplemente use la tabla "Libro" y agregue las columnas de "AudioBook", si corresponde. Si tiene que diferenciarse en el nivel de la tabla con columnas muy específicas, cree vistas. ¿Has verificado si un "Libro" y un "Libro de audio" con el mismo contenido tienen el mismo ISBN?

+2

Aunque su respuesta es técnicamente correcta, no creo que deba seguirse. PostgreSQL permite modelar esto bastante bien. Plegar múltiples objetos en una sola mesa generalmente termina en un gran desastre. – Theuni

Cuestiones relacionadas