sql - una - relacion varios a varios
¿Cómo manejas las relaciones m..n en una base de datos relacional? (10)
Agregue otra tabla llamada BookAuthors con columnas para BookID, AuthorID y NameUsed. Un valor NULL para NameUsed significaría extraerlo de la tabla del autor. Esto se llama una tabla de Intersección.
Veamos un ejemplo: libros. Un libro puede tener 1..n autores. Un autor puede tener 1..m libros. ¿Cuál es una buena manera de representar a todos los autores de un libro?
Se me ocurrió una idea para crear una mesa de Libros y una de Autores. La tabla de autores tiene una clave primaria de AuthorID con el nombre del autor. La tabla Libros tiene una ID de libro primaria y metadatos sobre el libro (título, fecha de publicación, etc.). Sin embargo, debe existir una forma de vincular los libros con los autores y autores con los libros. Y aquí es donde está el problema.
Digamos que tenemos tres libros de Bob. Sin embargo, en un libro, lo escribió como Bob, PhD. Otro escribió como el Dr. Bob, y un tercero escribió como el Dr. Robert. Quiero poder identificar el hecho de que estos autores son, en realidad, la misma persona, pero se les acredita con diferentes nombres. También quiero distinguir a Bob de otro Bob que escribió libros diferentes.
Ahora agreguemos también otra parte a una aplicación, una tabla Person que rastrea personas interesantes. Y digamos que Bob es una persona interesante. No solo quiero decir que el autor de los tres libros es Bob, sino que este interesante Bob es el mismo Bob que el autor Bob.
Entonces, ¿qué estrategias existen para un mapeo potencialmente complicado, mientras se asegura que los autores del libro se identifiquen con el nombre en la portada?
Creo que estás más o menos allí. Necesita una tabla Libros, una tabla Autores y luego una tabla "autores_de_libros" con la clave principal del libro, la clave principal del autor y un campo de texto "citado como" que muestra cómo ese autor en particular fue citado en ese libro .
Dado que el Dr. Bob y el Dr. Robert y Bob, PhD son todos la misma persona, se vincularían a la misma fila en la tabla de autores.
Sin embargo, creo que lo que necesita es una tabla de personas a la que los autores puedan vincular. También podría vincular su tabla de personas interesantes a ella. De esta forma, el autor Bob y el autor Robert, así como el interesante Bob, se unen a Person Bob. Espero que tenga sentido.
Esto suena como una relación de muchos a muchos, no de uno a muchos. Creo que querrás usar una tabla entre esos dos que te permita definir 1-a-muchos en cualquier lado de eso. Mira esto...
http://www.tekstenuitleg.net/en/articles/database_design_tutorial/8
Lo primero que se me ocurre es tener una tabla de enlaces, tal vez llamada AuthorOf para vincular libros con sus autores.
Las columnas serían AuthorID, BookID y quizás CreditAs, para que pueda diferenciar entre Dr. Bob y Bob, PhD. (Además de los nombres de pluma como Stephen King y Richard Bachman).
Y aún puede identificar al autor de manera única.
Lo que estás preguntando realmente no es cómo lidias con las relaciones 1.n, sino también con las relaciones (como efectivamente con el autor y con muchos libros, y un libro puede tener muchos autores).
La forma clásica de manejar esto es a través de una tabla intermedia, por lo que
Tabla de autor (authorID, authorDetails) Tabla de libro (bookID, detalles del libro) Tabla de AuthorBook (ID de autor, bookID)
Si realmente te molesta cambiar los nombres de los autores, utiliza una tabla de detalles del autor 1.n, por lo tanto, agrega
AuthorDetails (authorID, itemID, authorDetails)
y eliminar authorDetails de la tabla de autores
Necesitarías tres tablas:
- Libro
- Autor
- BookAuthors
El libro contendría la identificación del libro, el título del libro y toda la demás información que necesita recopilar sobre el libro.
El autor debería contener la identificación del autor, así como otra información, como el nombre, el apellido que debe recopilar sobre un autor determinado.
BookAuthors sería una unión de muchos a muchos, que contiene BookID, AuthorID y NameUsed. Esto permitiría que el libro tenga cero o muchos autores, para que un autor tenga cero o muchos libros, y que se capture información sobre esa relación. También podría, por ejemplo, tener una columna en la tabla BookAuthor que describa la relación del autor con el libro ("Editado por", "Fore word by").
Para la relación 1..n (el autor tiene muchos libros, el autor tiene muchos alias):
- Coloque una clave foránea author_id en Libros que apuntan al autor.
- Cree una nueva tabla, author_aliases, para contener la información de los alias.
- Coloque una clave foránea alias_id en Libros que apunten a un alias (se puede anular si los detalles del autor son predeterminados).
- Ponga la clave foránea author_id en author_aliases.
Si lo desea, puede usar tablas intermedias para vincular autores y libros, pero con un mapeo 1..n no creo que sea necesario.
Para una relación n..m (el autor tiene muchos libros, el libro tiene muchos autores):
Debería utilizar una tabla de combinación intermediaria (author_id, alias_id, book_id) en lugar de claves externas en la tabla de libros. Querrá mantener la clave externa de los alias al autor (para buscar fácilmente los alias del autor sin tener que pasar por todos sus libros).
Puede argumentar que, en términos de escalabilidad en el futuro, esta es una mejor manera de comenzar también, incluso si la especificación inicial dice que algo es una relación 1..n. Encontrará que las especificaciones (o preguntas) que se brindan a menudo son inadecuadas, por lo que deberá diseñar de manera general para cuando las especificaciones cambien o se aclaren.
Realmente parece que desea crear una serie de tablas de combinación personalizadas, que se utilizan para asociar elementos de una entidad a otra.
Comenzaría en el nivel más granular, persona y decir que CUALQUIER autor debe ser una persona. Simplificaría ese proceso.
Cree una tabla de persona con información de persona y una PersonId, coloque la información allí.
A continuación, cree una tabla BookAuthors, con 3 columnas BookId, PersonId, TitledName. De esta forma puede usar un nombre diferente si es necesario, de lo contrario, puede usar COALESE o algo similar para obtener el nombre predeterminado si TitledName es nulo.
Solo una idea..
una posible implementación en postgresql, solo por diversión:
create table books (
book_id integer primary key,
title varchar not null
);
create table aliases (
alias_id integer primary key,
alias varchar not null
);
create table books_aliases (
book_id integer references books (book_id),
alias_id integer references aliases (alias_id),
primary key (book_id, alias_id)
);
create table authors (
author_id integer primary key,
author varchar not null,
interesting boolean default false
);
create table aliases_authors (
alias_id integer references aliases (alias_id),
author_id integer references authors (author_id),
primary key (alias_id, author_id)
);
create view books_aliases_authors as
select * from books
natural join books_aliases
natural join aliases
natural join aliases_authors
natural join authors;
uno podría usar "usar" en lugar de la combinación natural:
create view books_aliases_authors as
select *
from books
join books_aliases using (book_id)
join aliases using (alias_id)
join aliases_authors using (alias_id)
join authors using (author_id);
o haga lo complicado para la compatibilidad con mysql (tenga en cuenta que mysql también necesitaría una longitud máxima explícita para los varchars anteriores):
create view books_aliases_authors as
select b.book_id, title, l.alias_id, alias, t.author_id, author, interesting
from books b
join books_aliases bl on bl.book_id = b.book_id
join aliases l on bl.alias_id = l.alias_id
join aliases_authors lt on lt.alias_id = l.alias_id
join authors t on t.author_id = lt.author_id;
este ejemplo no usa la tabla "personas", solo una bandera "interesante" para los autores. tenga en cuenta que nada cambia (estructuralmente), si cambia el nombre de "autores" a "personas"