2009-02-18 43 views
22

Actualmente tengo varias tablas en mi base de datos que se componen de los mismos 'campos básicos' como:Polimorfismo en tablas de bases de datos SQL?

name character varying(100), 
description text, 
url character varying(255)

Pero tengo múltiples especializaciones de esa tabla básica, que es por ejemplo que tv_series tiene los campos season, episode , airing, mientras que la mesa tiene moviesrelease_date, etc. budget

Ahora al principio esto no es un problema, pero quiero crear una segunda tabla, llamada linkgroups con una clave externa a estas tablas especializadas. Eso significa que de alguna manera tendría que normalizarlo dentro de sí mismo.

Una forma de resolver esto que he escuchado es normalizarlo con un key-value -pair-table, pero no me gusta esa idea ya que es una especie de esquema 'base de datos dentro de una base de datos', no tiene una forma de requerir ciertas claves/campos ni requiere un tipo especial, y sería una gran molestia buscar y ordenar los datos más tarde.

Así que estoy buscando una forma de 'compartir' una clave principal entre varias tablas o incluso mejor: una forma de normalizarlo teniendo una tabla general y varias tablas especializadas.

+0

Ok, no estoy 100% seguro de estar leyendo este mensaje. Tienes un montón de tablas dispares que tienen una estructura común. Desea agregar una sola tabla que haga referencia a estas tablas, pero no puede usar una FK porque cada tabla tiene su propia PK. Si puedes, crearía una tabla maestra común y la colgaría. – Joe

+0

Sí, pero mi pregunta es cómo exactamente hacerlo de la manera más elegante –

Respuesta

24

Correcto, el problema es que quiere que solo un objeto de un subtipo haga referencia a cualquier fila dada de la clase padre. A partir de la example propuesta por @Jay S, intente esto:

create table media_types (
    media_type  int primary key, 
    media_name  varchar(20) 
); 
insert into media_types (media_type, media_name) values 
    (2, 'TV series'), 
    (3, 'movie'); 

create table media (
    media_id  int not null, 
    media_type  not null, 
    name   varchar(100), 
    description text, 
    url   varchar(255), 
    primary key (media_id, media_type), 
    foreign key (media_type) 
    references media_types (media_type) 
); 

create table tv_series (
    media_id  int primary key, 
    media_type  int check (media_type = 2), 
    season   int, 
    episode  int, 
    airing   date, 
    foreign key (media_id, media_type) 
    references media (media_id, media_type) 
); 

create table movies (
    media_id  int primary key, 
    media_type  int check (media_type = 3), 
    release_date date, 
    budget   numeric(9,2), 
    foreign key (media_id, media_type) 
    references media (media_id, media_type) 
); 

Este es un ejemplo de los subtipos disjuntos mentioned por @ Mike g.


Re comentarios de @Countably Infinito y @Peter:

INSERT o dos mesas requerirían dos instrucciones de inserción. Pero eso también es cierto en SQL cada vez que tiene tablas secundarias. Es algo común de hacer.

La ACTUALIZACIÓN puede requerir dos instrucciones, pero algunas marcas de RDBMS admiten la ACTUALIZACIÓN de varias tablas con la sintaxis JOIN, por lo que puede hacerlo en una sola declaración.

Al consultar los datos, puede hacerlo simplemente consultando la tabla media si sólo se necesita información sobre las columnas comunes:

SELECT name, url FROM media WHERE media_id = ? 

Si sabe que está consultando una película, se puede conseguir-película específica información con una única combinación:

SELECT m.name, v.release_date 
FROM media AS m 
INNER JOIN movies AS v USING (media_id) 
WHERE m.media_id = ? 

Si desea información para una entrada de los medios de comunicación dado, y no se sabe de qué tipo es, usted tiene que unirse a todas las tablas del subtipo, sabiendo que sólo uno dicha tabla de subtipos coincidirá:

SELECT m.name, t.episode, v.release_date 
FROM media AS m 
LEFT OUTER JOIN tv_series AS t USING (media_id) 
LEFT OUTER JOIN movies AS v USING (media_id) 
WHERE m.media_id = ? 

Si el medio determinado es una película, todas las columnas en t.* serán NULL.

+0

¿Alguna información sobre el soporte de JDBC? ¿Simplemente pasará consultas y actualizaciones al DBMS, incluso si se usan anotaciones especiales para subtipos? ¿O habrá problemas? –

+0

No, necesita escribir el SQL correcto. JDBC hace muy poco con respecto a la reescritura de consultas. No admite anotaciones, solo algunas secuencias de escape para ayudar a la compatibilidad del proveedor. –

+0

¿Entonces el conjunto de resultados tendrá solo valores nulos para atributos no utilizados por los subtipos actuales? ¿Y cómo leerá el subtipo en sí, a través del atributo comprobado? –

1

Puede crear una tabla con los campos principales más un uid y luego las tablas de extensión con el mismo uid para cada caso específico. Para consultar estos como tablas separadas, puede crear vistas.

8

Considere utilizar una tabla de datos básicos principal con tablas que se extienden con información especializada.

Ej.

basic_data 
id int, 
name character varying(100), 
description text, 
url character varying(255) 


tv_series 
id int, 
BDID int, --foreign key to basic_data 
season, 
episode 
airing 


movies 
id int, 
BDID int, --foreign key to basic_data 
release_data 
budget 
+0

Gracias, ya lo he pensado antes, el único problema que he tenido con esto es que teóricamente da la posibilidad de tener múltiples entradas en las tablas para una en basic_data (es decir, podría haber una entrada en tv_series y películas). Si nada mejor viene, lo haré de esta manera. –

+0

Y también esta es una relación unidireccional ya que solo las tablas especializadas apuntan a la tabla general. –

+0

Voto, aunque sugeriría que tenga cuidado con la tabla "basic_data" para que no sea demasiado genérica. Si se trata de "medios" o algo así, tiene sentido. No intentes forzar cosas que no tengan sentido. –

2

Lo que está buscando se llama 'subtipos disjuntos' en el mundo relacional. No son compatibles en sql en el nivel de idioma, pero pueden ser más o menos implemented on top of sql.

1

Usando el enfoque de subtipo disjunto sugerido por Bill Karwin, ¿cómo harías INSERTES y ACTUALIZACIONES sin tener que hacerlo en dos pasos?

Obteniendo datos, puedo presentar una vista que se une y selecciona en función de un tipo de medio específico pero AFAIK No puedo actualizar o insertar en esa vista porque afecta a varias tablas (estoy hablando de MS SQL Server aquí). ¿Se puede hacer esto sin hacer dos operaciones, y sin un procedimiento almacenado, naturalmente?

Gracias

+0

Bueno, supongo que podría usar un activador en lugar de un disparador para que esto funcione, pero significa que tendré que codificarlos para cada subtipo que tengo. Y también, creo que los desencadenantes son tan "secretos" cuando se depuran ... ¿Hay una manera mejor? – Peter

1

pregunta es bastante viejo, pero para las versiones modernas PostreSQL también vale la pena considerar el uso de tipo JSON/jsonb/hstore. Por ejemplo:

create table some_table (
    name character varying(100), 
    description text, 
    url character varying(255), 
    additional_data json 
); 
Cuestiones relacionadas