Una relación "muchos a muchos" es cuando un registro en una tabla puede estar relacionado con varios registros en otra tabla, y viceversa. Por ejemplo: - Un estudiante (de la tabla students) puede estar inscrito en varios cursos (de la tabla courses). - Un curso puede ser tomado por varios estudiantes.
El problema es que guardar esa relación directamente es un lío. Por eso, lo que nos salva es una tabla intermedia que almacena esas relaciones.
Ejemplo de la vida real
Imagina que tienes una tabla de estudiantes y una tabla de cursos. Si intentas añadir en cada tabla una columna para guardar todos los datos relacionados, será un caos:
- En la tabla
studentstendrías que guardar una lista de todos los cursos en los que está inscrito el estudiante. ¿Pero cómo guardas una lista? ¿Separado por comas? ¿En un array? Eso sería complicado para hacer consultas. - En la tabla
coursestendrías que guardar una lista de estudiantes, y eso sí que es un dolor de cabeza.
Así que la solución correcta es crear una tercera tabla que guarde las relaciones entre estudiantes y cursos.
¡La tabla intermedia al rescate!
La tabla intermedia (a veces llamada tabla de relaciones) resuelve todos los problemas. Contiene dos referencias externas:
- Una clave foránea a la tabla
students. - Una clave foránea a la tabla
courses.
Cada registro en esta tabla crea una relación entre un estudiante concreto y un curso concreto.
Creando tablas para la relación "muchos a muchos"
¡Vamos a practicar! Así es como podemos crear tablas para las relaciones entre estudiantes y cursos:
Paso 1: Crear la tabla students
Aquí está nuestra tabla de estudiantes. Aquí guardamos los identificadores únicos de los estudiantes y sus nombres.
CREATE TABLE students (
student_id SERIAL PRIMARY KEY,
name TEXT NOT NULL
);
student_id— es el identificador único del estudiante (autoincremental, gracias aSERIAL!).name— el nombre del estudiante.
Paso 2: Crear la tabla courses
Ahora creamos la tabla de cursos. Aquí guardamos los identificadores únicos de los cursos y sus títulos.
CREATE TABLE courses (
course_id SERIAL PRIMARY KEY,
title TEXT NOT NULL
);
course_id— identificador único del curso.title— nombre del curso.
Paso 3: Crear la tabla intermedia enrollments
Ahora creamos nuestra mágica tabla de relaciones. Tiene dos columnas, cada una es una clave foránea que apunta a las tablas correspondientes.
CREATE TABLE enrollments (
student_id INT REFERENCES students(student_id),
course_id INT REFERENCES courses(course_id),
PRIMARY KEY (student_id, course_id)
);
Vamos a ver la estructura:
student_id— clave foránea que referencia astudent_idde la tablastudents.course_id— clave foránea que referencia acourse_idde la tablacourses.PRIMARY KEY (student_id, course_id)— la clave primaria es la combinación de las dos claves foráneas. Así nos aseguramos de que cada relación sea única.
Insertando datos
Vamos a meter algunos datos para ver cómo funciona todo esto.
Paso 1: Añadir estudiantes
INSERT INTO students (name) VALUES
('Alice'),
('Bob'),
('Charlie');
Resultado:
| student_id | name |
|---|---|
| 1 | Alice |
| 2 | Bob |
| 3 | Charlie |
Paso 2: Añadir cursos
INSERT INTO courses (title) VALUES
('Mathematics'),
('History'),
('Biology');
Resultado:
| course_id | title |
|---|---|
| 1 | Mathematics |
| 2 | History |
| 3 | Biology |
Paso 3: Añadir registros en enrollments
Ahora vamos a inscribir estudiantes en cursos. Por ejemplo:
Aliceestá inscrita enMathematicsyHistory.Bobestá inscrito solo enBiology.Charlieestá inscrito en los tres cursos.
INSERT INTO enrollments (student_id, course_id) VALUES
(1, 1), -- Alice en Mathematics
(1, 2), -- Alice en History
(2, 3), -- Bob en Biology
(3, 1), -- Charlie en Mathematics
(3, 2), -- Charlie en History
(3, 3); -- Charlie en Biology
Resultado:
| student_id | course_id |
|---|---|
| 1 | 1 |
| 1 | 2 |
| 2 | 3 |
| 3 | 1 |
| 3 | 2 |
| 3 | 3 |
Consultas para la relación "muchos a muchos"
Ahora que tenemos datos, ¡vamos a sacarles provecho!
¿Cómo encontrar todos los cursos en los que está inscrito un estudiante?
Por ejemplo, para saber en qué cursos está inscrita Alice (ID = 1), haz esta consulta:
SELECT c.title
FROM courses c
JOIN enrollments e ON c.course_id = e.course_id
WHERE e.student_id = 1;
Resultado:
| title |
|---|
| Mathematics |
| History |
¿Cómo encontrar todos los estudiantes inscritos en un curso concreto?
Por ejemplo, si quieres saber quién está inscrito en el curso Mathematics (ID = 1):
SELECT s.name
FROM students s
JOIN enrollments e ON s.student_id = e.student_id
WHERE e.course_id = 1;
Resultado:
| name |
|---|
| Alice |
| Charlie |
¿Cómo encontrar los estudiantes y sus cursos?
Para ver el panorama completo de quién está inscrito en qué, haz esta consulta:
SELECT s.name AS student, c.title AS course
FROM students s
JOIN enrollments e ON s.student_id = e.student_id
JOIN courses c ON e.course_id = c.course_id;
Resultado:
| student | course |
|---|---|
| Alice | Mathematics |
| Alice | History |
| Bob | Biology |
| Charlie | Mathematics |
| Charlie | History |
| Charlie | Biology |
La tabla enrollments hace que nuestro esquema sea flexible — podemos añadir o quitar relaciones entre estudiantes y cursos sin tocar las tablas principales. Gracias a las consultas JOIN es fácil encontrar los datos que necesitas, por ejemplo, quién está inscrito en qué curso. Y las claves foráneas se encargan de que no haya errores — por ejemplo, que nadie inscriba a un estudiante en un curso que ni siquiera existe.
Errores típicos en relaciones "muchos a muchos"
Falta de restricción única: Si no pones PRIMARY KEY, puedes meter la misma relación varias veces por accidente.
Violación de la integridad de los datos: Si intentas insertar un registro con un student_id o course_id que no existe, te dará error.
Eliminar datos en el orden incorrecto: Si borras primero un curso de courses, los registros en enrollments se quedan "huérfanos". Para evitar esto, usa ON DELETE CASCADE en la definición de las claves foráneas.
GO TO FULL VERSION