Relaciones en MySQL
Las bases de datos relacionales deben su nombre a que las tablas se conectan entre sí mediante relaciones. Aprendé cómo funcionan esas conexiones, cómo se protegen con foreign keys y cómo diseñar una base de datos bien estructurada.
¿Qué es una relación?
Una relación es una conexión entre dos tablas que comparten datos en común.
En nuestra base de datos tutorial_db, por ejemplo, cada orden pertenece
a un cliente. Esa conexión se establece guardando el id del cliente
dentro de la tabla ordenes:
-- tabla clientes -- tabla ordenes
-- id | nombre -- id | id_cliente | total
-- 1 | Ana García -- 1 | 3 | 450.00
-- 2 | Carlos López -- 2 | 1 | 1200.00
-- 3 | Beatriz Torres -- 3 | 1 | 89.00
La columna id_cliente en ordenes se llama
clave foránea o foreign key: es una referencia
al id de la tabla clientes. Ese id al que
apunta se llama clave primaria o primary key.
La Primary Key identifica de forma única cada fila dentro de su tabla — ningún dos registros pueden tener el mismo id. La Foreign Key es una columna en otra tabla que guarda ese id para establecer la relación. Una tabla puede tener muchas foreign keys pero solo una primary key.
Tipos de relaciones
Las relaciones entre tablas se clasifican según cuántos registros de cada lado pueden participar en la relación.
Uno a Muchos (1:N) — la más común
Un registro de la tabla A puede relacionarse con muchos registros de la tabla B, pero cada registro de B pertenece a uno solo de A.
-- Un cliente puede tener MUCHAS órdenes
-- Pero cada orden pertenece a UN SOLO cliente
-- clientes (1) ←→ (N) ordenes
-- clientes (1) ←→ (N) ... (relación 1 a muchos)
En nuestra base de datos todas las relaciones son 1:N:
- Un cliente tiene muchas órdenes
- Una categoría tiene muchos productos
- Una orden tiene muchos detalles
- Un producto aparece en muchos detalles
Muchos a Muchos (N:M)
Un registro de A puede relacionarse con muchos de B, y viceversa. Este tipo de relación no puede representarse directamente con una foreign key — necesita una tabla intermedia que registra cada par de relaciones.
En nuestra base, la relación entre ordenes y productos
es N:M: una orden puede tener muchos productos, y un producto puede aparecer
en muchas órdenes. La tabla detalle_ordenes es la tabla intermedia:
-- ordenes (N) ←→ detalle_ordenes ←→ (M) productos
--
-- detalle_ordenes tiene:
-- id_orden → foreign key a ordenes
-- id_producto → foreign key a productos
-- cantidad
-- precio_unitario
Uno a Uno (1:1)
Cada registro de A tiene exactamente un registro en B y viceversa. Se usa para separar datos de una misma entidad cuando hay demasiadas columnas, o cuando ciertos datos son opcionales o sensibles.
-- Ejemplo: tabla usuarios y tabla perfiles
-- Cada usuario tiene exactamente un perfil
-- usuarios (1) ←→ (1) perfiles
Hacete estas preguntas: "¿Un cliente puede tener muchas órdenes?" → Sí. "¿Una orden puede pertenecer a muchos clientes?" → No. Entonces es 1:N con la foreign key en el lado "muchos" (en ordenes).
Integridad referencial
La integridad referencial garantiza que las relaciones entre tablas sean siempre consistentes. En términos prácticos, significa dos cosas:
- No podés insertar una orden con un
id_cliente = 999si ese cliente no existe - No podés eliminar un cliente que todavía tiene órdenes asociadas
MySQL protege estas reglas automáticamente cuando definís una FOREIGN KEY con la opción adecuada.
-- Al crear la tabla ordenes, definimos la foreign key:
CREATE TABLE ordenes (
id INT AUTO_INCREMENT PRIMARY KEY,
id_cliente INT NOT NULL,
fecha DATE,
total DECIMAL(10,2),
FOREIGN KEY (id_cliente)
REFERENCES clientes(id)
ON DELETE RESTRICT -- no permite borrar el cliente si tiene órdenes
ON UPDATE CASCADE -- si cambia el id del cliente, se actualiza en ordenes
);
RESTRICT — rechaza la operación si hay registros relacionados (la opción más segura).
CASCADE — propaga el cambio: si borrás el cliente, borra todas sus órdenes también.
SET NULL — pone NULL en la foreign key de los registros relacionados.
NO ACTION — igual que RESTRICT en MySQL.
Ver las relaciones de una base de datos
Podés consultar las foreign keys existentes de dos formas.
La primera es con SHOW CREATE TABLE, que muestra el SQL completo
con el que fue creada la tabla, incluyendo las foreign keys:
SHOW CREATE TABLE ordenes;
La segunda forma, más detallada, es consultar el esquema de información de MySQL que guarda metadata sobre todas las tablas y sus relaciones:
SELECT
TABLE_NAME AS tabla,
COLUMN_NAME AS columna_fk,
REFERENCED_TABLE_NAME AS tabla_referenciada,
REFERENCED_COLUMN_NAME AS columna_referenciada,
CONSTRAINT_NAME AS nombre_constraint
FROM information_schema.KEY_COLUMN_USAGE
WHERE TABLE_SCHEMA = DATABASE()
AND REFERENCED_TABLE_NAME IS NOT NULL
ORDER BY TABLE_NAME;
information_schema es una base de datos especial que MySQL mantiene automáticamente. Contiene información sobre todas tus tablas, columnas, índices, foreign keys y más. Es de solo lectura — no podés modificarla, solo consultarla.
Normalización — organizar bien los datos
La normalización es el proceso de organizar las tablas para eliminar redundancia y evitar inconsistencias. No es una regla rígida, sino una guía para tomar buenas decisiones de diseño.
El problema que resuelve
Imaginá esta tabla sin normalizar:
-- MAL DISEÑO: todo en una sola tabla
-- id | cliente_nombre | cliente_email | producto | categoria | precio
-- 1 | Ana García | ana@mail.com | Laptop | Electrónica | 1200
-- 2 | Ana García | ana@mail.com | Mouse | Electrónica | 25
-- 3 | Carlos López | carlos@mail.com | Laptop | Electrónica | 1200
Los problemas son evidentes:
- El nombre y email de Ana se repiten en cada compra — si cambia el email, hay que actualizarlo en todas sus filas
- "Electrónica" se repite en cada producto de esa categoría — si querés renombrarla, tenés que tocar miles de filas
- El precio de la Laptop aparece en cada venta — si cambia, ¿cuál es el precio real?
El diseño normalizado
Cada entidad vive en su propia tabla. Los datos se relacionan mediante foreign keys en lugar de repetirse:
-- clientes: cada cliente existe una sola vez
-- id | nombre | email
-- 1 | Ana García | ana@mail.com
-- 2 | Carlos López | carlos@mail.com
-- categorias: cada categoría existe una sola vez
-- id | nombre
-- 1 | Electrónica
-- productos: cada producto existe una sola vez
-- id | nombre | precio | id_categoria
-- 1 | Laptop | 1200 | 1
-- 2 | Mouse | 25 | 1
-- ordenes + detalle_ordenes: registran qué compró quién
Ahora si Ana cambia su email, se actualiza en un solo lugar. Si "Electrónica" se renombra a "Tecnología", se cambia en una sola fila.
Si te encontrás copiando el mismo dato en múltiples filas, probablemente necesitás una tabla separada y una foreign key. El principio es: cada dato debe existir en un único lugar.
Ejemplo Completo — analizar la estructura de tutorial_db
Con todo lo aprendido, podés hacer una query que muestre el mapa completo de relaciones de la base de datos: cuántas filas tiene cada tabla y cómo se conectan entre sí.
-- Contar registros en cada tabla para entender la escala
SELECT 'clientes' AS tabla, COUNT(*) AS registros FROM clientes
UNION ALL
SELECT 'categorias', COUNT(*) FROM categorias
UNION ALL
SELECT 'productos', COUNT(*) FROM productos
UNION ALL
SELECT 'ordenes', COUNT(*) FROM ordenes
UNION ALL
SELECT 'detalle_ordenes', COUNT(*) FROM detalle_ordenes
ORDER BY registros DESC;
Probá el Ejemplo 4: Mapa de la base de datos
Buenas Prácticas
- Siempre definí PRIMARY KEY — cada tabla debería tener un identificador único, generalmente
id INT AUTO_INCREMENT - Usá FOREIGN KEY con RESTRICT por defecto — es la opción más segura; evita borrados accidentales en cascada
- Nombrá las constraints descriptivamente —
fk_ordenes_clienteses mejor que dejar que MySQL le ponga un nombre automático - Un dato, un lugar — si repetís el mismo dato en varias filas, algo en el diseño está mal
- Indexá las foreign keys — MySQL lo hace automáticamente en InnoDB, pero si usás otro motor hay que hacerlo a mano
- Dibujá el diagrama antes de crear las tablas — un papel con las entidades y sus relaciones ahorra muchos problemas después