Hoy vamos a profundizar en un tema súper importante: cómo modelar relaciones entre tablas. La normalización no es solo sobre datos atómicos y eliminar redundancias, sino también sobre crear relaciones correctas entre tablas.
Si una base de datos es un sistema organizado para guardar información, entonces las relaciones entre tablas son como puentes lógicos que muestran cómo los datos interactúan entre sí. Imagínate una biblioteca donde la info sobre los libros se guarda aparte de la info sobre los autores, pero cada libro "conoce" a su autor a través de una relación especial. O piensa en una tienda online: los datos de los productos existen independientemente de la info de los clientes, pero cuando alguien hace un pedido, el sistema conecta a un cliente concreto con productos concretos a través de la tabla de pedidos.
En una clínica médica, los pacientes están conectados con sus historiales médicos, los médicos con el horario de citas, y los medicamentos con las prescripciones. Estas relaciones ayudan al sistema a entender qué info pertenece a qué, sin duplicar datos innecesariamente.
Los tipos principales de estas relaciones funcionan como relaciones en la vida real: un pasaporte pertenece solo a una persona (uno a uno), un profe puede dar varias asignaturas (uno a muchos), y los estudiantes pueden apuntarse a diferentes materias, mientras que cada materia la cursan diferentes estudiantes (muchos a muchos).
Uno a uno (1:1)
Esta es una relación donde un registro en la tabla "A" corresponde exactamente a un registro en la tabla "B". Por ejemplo, tenemos las tablas "Empleados" y "Datos de pasaporte". Un empleado solo puede tener un pasaporte, y cada pasaporte pertenece a un solo empleado.
Ejemplo:
Empleados
| id | nombre | puesto |
|---|---|---|
| 1 | Otto Lin | manager |
Datos de pasaporte
| id | empleado_id | número de pasaporte |
|---|---|---|
| 1 | 1 | 123456789 |
Aquí la relación se hace a través de la clave foránea empleado_id, que apunta al id en la tabla "Empleados".
Uno a muchos (1:N)
Este es el tipo de relación más común. Aquí cada registro de la tabla "A" puede estar relacionado con varios registros en la tabla "B", pero cada registro de la tabla "B" solo está relacionado con un registro de la tabla "A". Por ejemplo, tenemos las tablas "Profesores" y "Cursos". Un profe puede dar varios cursos.
Ejemplo:
Profesores
| id | nombre |
|---|---|
| 1 | Anna Song |
| 2 | Aleks Min |
Cursos
| id | nombre del curso | profesor_id |
|---|---|---|
| 1 | Fundamentos de SQL | 1 |
| 2 | Administración de BD | 1 |
| 3 | Programación en Python | 2 |
La relación se crea a través de la clave foránea profesor_id en la tabla "Cursos".
Muchos a muchos (M:N)
Cuando tienes un montón de todo, mola pero es complicado. Aquí cada registro en la tabla "A" puede estar relacionado con varios registros de la tabla "B", y viceversa. Por ejemplo, los estudiantes pueden apuntarse a varios cursos, y cada curso puede tener varios estudiantes.
Ejemplo:
Estudiantes
| id | nombre |
|---|---|
| 1 | Otto Lin |
| 2 | Maria Chi |
Cursos
| id | nombre del curso |
|---|---|
| 1 | Fundamentos de SQL |
| 2 | Administración de BD |
Para conectar todo esto necesitamos una tabla intermedia que guarde las relaciones entre estudiantes y cursos:
Inscripciones
| id | estudiante_id | curso_id |
|---|---|---|
| 1 | 1 | 1 |
| 2 | 1 | 2 |
| 3 | 2 | 1 |
Modelando relaciones con claves foráneas
Una clave foránea es una columna (o conjunto de columnas) que apunta a la columna clave primaria en otra tabla. Es la base para construir relaciones entre tablas.
Ejemplo de clave foránea:
CREATE TABLE Cursos (
id SERIAL PRIMARY KEY,
nombre VARCHAR(255)
);
CREATE TABLE Inscripciones (
id SERIAL PRIMARY KEY,
estudiante_id INT,
curso_id INT,
FOREIGN KEY (curso_id) REFERENCES Cursos(id)
);
¿Cómo evitar errores al diseñar claves foráneas? Lo primero es comprobar que los tipos de datos entre las columnas de la clave foránea y la clave primaria coincidan — si no, la base de datos no te dejará crear la relación. Y también es importante pensar de antemano qué debe pasar cuando se borren registros. Por ejemplo, si borras una fila de la tabla padre, ¿qué pasa con los registros hijos? Una opción muy usada es poner ON DELETE CASCADE, para que los datos relacionados se borren automáticamente junto con el registro principal. Así mantienes el orden y evitas referencias "colgantes".
Implementando la relación "Muchos a muchos"
Pongamos un ejemplo: tenemos estudiantes y cursos. Un estudiante puede estar inscrito en varios cursos, y un curso puede tener varios estudiantes inscritos. Para la relación M:N creamos tres tablas: Estudiantes, Cursos e Inscripciones.
CREATE TABLE Estudiantes (
id SERIAL PRIMARY KEY,
nombre VARCHAR(255)
);
CREATE TABLE Cursos (
id SERIAL PRIMARY KEY,
nombre VARCHAR(255)
);
CREATE TABLE Inscripciones (
id SERIAL PRIMARY KEY,
estudiante_id INT,
curso_id INT,
FOREIGN KEY (estudiante_id) REFERENCES Estudiantes(id),
FOREIGN KEY (curso_id) REFERENCES Cursos(id)
);
Ahora podemos añadir registros a la tabla Inscripciones para conectar estudiantes y cursos.
Ejercicio práctico
Crea la estructura de base de datos para un sistema de gestión de cursos. Debes tener las tablas Estudiantes, Cursos e Inscripciones. Implementa todas las relaciones entre las tablas. Luego mete algunos datos de ejemplo de estudiantes, cursos y sus inscripciones. Vamos a ver cómo se hace.
- Creamos las tablas:
CREATE TABLE Estudiantes (
id SERIAL PRIMARY KEY,
nombre VARCHAR(255)
);
CREATE TABLE Cursos (
id SERIAL PRIMARY KEY,
nombre VARCHAR(255)
);
CREATE TABLE Inscripciones (
id SERIAL PRIMARY KEY,
estudiante_id INT,
curso_id INT,
FOREIGN KEY (estudiante_id) REFERENCES Estudiantes(id),
FOREIGN KEY (curso_id) REFERENCES Cursos(id)
);
- Insertamos datos:
INSERT INTO Estudiantes (nombre) VALUES ('Otto Lin'), ('Maria Chi');
INSERT INTO Cursos (nombre) VALUES ('Fundamentos de SQL'), ('Administración de BD');
INSERT INTO Inscripciones (estudiante_id, curso_id) VALUES (1, 1), (1, 2), (2, 1);
- Comprobamos los datos:
SELECT
Estudiantes.nombre AS estudiante,
Cursos.nombre AS curso
FROM Inscripciones
JOIN Estudiantes ON Inscripciones.estudiante_id = Estudiantes.id
JOIN Cursos ON Inscripciones.curso_id = Cursos.id;
Resultado:
| estudiante | curso |
|---|---|
| Otto Lin | Fundamentos de SQL |
| Otto Lin | Administración de BD |
| Maria Chi | Fundamentos de SQL |
Dificultades y particularidades al modelar relaciones
Cuando modelas relaciones entre tablas, pueden surgir problemas como:
- Errores al borrar datos (por ejemplo, tienes registros en la tabla hija que dependen de un registro en la tabla padre).
- Rendimiento de las consultas cuando hay muchos datos. Las relaciones M:N son especialmente "tragonas", porque requieren joins extra.
Para solucionar estos problemas ayudan:
- Usar índices en las claves foráneas
- Una estructura de base de datos bien pensada.
- Equilibrio entre normalización y rendimiento.
Hemos visto cómo modelar relaciones entre tablas a un nivel muy básico y lo hemos puesto en práctica creando la estructura de una base de datos para un sistema de gestión de cursos. Me gustaría, claro, ver un ejemplo grande, pero no se me ocurre cómo hacerlo. Un ejemplo grande se vuelve complicado y aburrido. Y no sirve de mucho. Intentaré volver a este tema cerca del final del curso.
GO TO FULL VERSION