Una FOREIGN KEY (clave foránea) es una restricción que vincula una columna (o conjunto de columnas) de una tabla con la PRIMARY KEY (o una UNIQUE) de otra tabla. Su objetivo es garantizar la integridad referencial: que los registros hijos siempre apunten a un registro padre existente, y que no queden huérfanos cuando se borra o modifica el padre.
Gracias a las claves foráneas, el propio motor de base de datos evita inconsistencias lógicas que, de otro modo, tendrías que controlar manualmente desde la aplicación.
Conceptos clave
- Tabla padre (referenciada): la que expone la clave primaria/única (por ejemplo,
clientes(id)). - Tabla hija (referenciante): la que contiene la clave foránea (por ejemplo,
pedidos(cliente_id)). - Integridad referencial: el SGBD impide insertar/actualizar un hijo que apunte a un padre inexistente, y también controla qué ocurre si el padre cambia o se elimina.
- Acciones ON DELETE / ON UPDATE: reglas que definen el comportamiento ante cambios en el padre (
CASCADE,RESTRICT,SET NULL,SET DEFAULT, etc.).
Sintaxis básica (DDL)
Definir FOREIGN KEY al crear la tabla
CREATE TABLE clientes (
id INT PRIMARY KEY,
nombre VARCHAR(100) NOT NULL
);
CREATE TABLE pedidos (
id INT PRIMARY KEY,
cliente_id INT NOT NULL,
fecha DATE NOT NULL,
total DECIMAL(10,2) NOT NULL CHECK (total >= 0),
CONSTRAINT fk_pedidos_clientes
FOREIGN KEY (cliente_id)
REFERENCES clientes(id)
ON DELETE RESTRICT
ON UPDATE CASCADE
);
FOREIGN KEY (cliente_id)declara la columna referenciante.REFERENCES clientes(id)indica la columna referenciada (debe serPRIMARY KEYoUNIQUE).ON DELETEyON UPDATEmarcan el comportamiento ante cambios enclientes(id).
Agregar una FK con ALTER TABLE
ALTER TABLE pedidos
ADD CONSTRAINT fk_pedidos_clientes
FOREIGN KEY (cliente_id)
REFERENCES clientes(id)
ON DELETE CASCADE;
Quitar una FK
ALTER TABLE pedidos
DROP CONSTRAINT fk_pedidos_clientes;
(El nombre de la restricción puede variar si el motor lo generó automáticamente.)
Acciones ON DELETE / ON UPDATE
- RESTRICT (o NO ACTION): impide borrar/actualizar el padre si hay hijos que lo referencian. Es la opción más segura por defecto en muchos motores.
- CASCADE: al borrar/actualizar el padre, propaga la operación a los hijos (borra o actualiza la FK). Útil, pero requiere cuidado.
- SET NULL: al borrar/actualizar el padre, la FK del hijo se vuelve NULL (la FK debe permitir NULL).
- SET DEFAULT: establece un valor por defecto en la FK del hijo (esa columna debe tener
DEFAULTválido). - NO ACTION: similar a
RESTRICTen la mayoría de motores; la comprobación se difiere al final de la sentencia (no confundir con “no hacer nada”).
Consejo: Elegí la acción más alineada con tu negocio.
RESTRICTprotege;CASCADEautomatiza;SET NULL“desengancha” sin borrar.
Ejemplos prácticos
1) Pedidos y clientes (relación 1:N)
CREATE TABLE clientes (
id INT PRIMARY KEY,
nombre VARCHAR(100) NOT NULL
);
CREATE TABLE pedidos (
id INT PRIMARY KEY,
cliente_id INT NOT NULL,
fecha DATE NOT NULL,
total DECIMAL(10,2) NOT NULL,
CONSTRAINT fk_pedido_cliente
FOREIGN KEY (cliente_id) REFERENCES clientes(id)
ON DELETE RESTRICT
ON UPDATE CASCADE
);
- No se puede insertar un pedido con
cliente_idinexistente. - Si el
iddel cliente cambia (raro, pero posible), se actualiza enpedidos.
2) SET NULL para desasociar
CREATE TABLE productos (
id INT PRIMARY KEY,
nombre VARCHAR(120) NOT NULL
);
CREATE TABLE fotos_producto (
id INT PRIMARY KEY,
producto_id INT NULL,
url TEXT NOT NULL,
CONSTRAINT fk_foto_producto
FOREIGN KEY (producto_id) REFERENCES productos(id)
ON DELETE SET NULL
);
- Si se borra un producto, las fotos no se eliminan: se quedan con
producto_id = NULL.
3) Clave foránea compuesta
CREATE TABLE cursos (
codigo VARCHAR(10),
version INT,
titulo VARCHAR(200),
PRIMARY KEY (codigo, version)
);
CREATE TABLE inscripciones (
alumno_id INT,
curso_codigo VARCHAR(10),
curso_version INT,
fecha DATE NOT NULL,
PRIMARY KEY (alumno_id, curso_codigo, curso_version),
CONSTRAINT fk_insc_curso
FOREIGN KEY (curso_codigo, curso_version)
REFERENCES cursos(codigo, version)
ON DELETE RESTRICT
);
- La FK referencía dos columnas; deben coincidir en orden y tipos.
4) FK autocontenida (self-reference)
CREATE TABLE empleados (
id INT PRIMARY KEY,
nombre VARCHAR(100) NOT NULL,
jefe_id INT NULL,
CONSTRAINT fk_empleado_jefe
FOREIGN KEY (jefe_id) REFERENCES empleados(id)
ON DELETE SET NULL
);
- Un empleado puede tener como jefe a otro empleado.
- Al borrar un jefe, sus subordinados quedan con
jefe_id = NULL.
Diferencias entre motores (visión general)
- PostgreSQL: soporte sólido de FKs,
DEFERRABLE INITIALLY DEFERRED/IMMEDIATE(permite aplazar la validación hastaCOMMIT). - MySQL/MariaDB: FKs reales con InnoDB.
RESTRICT,CASCADE,SET NULL. Atención a tipos/longitudes: deben coincidir exactamente (collation/charset incluido en cadenas). - SQL Server: FKs con opciones de
ON DELETE/UPDATE. PermiteWITH NOCHECK(no recomendado en producción) para deshabilitar comprobaciones. - Oracle: soporte completo, acciones estándar. Puede diferir validaciones (
DEFERRABLE).
Reglas finas (p. ej., restricciones diferibles, nombre máximo de constraint, detalles de
NO ACTION) dependen del SGBD: revisá la documentación específica.
Índices y rendimiento
- Índice en la clave primaria/única de la tabla padre: obligado (PRIMARY/UNIQUE ya crean índice).
- Índice en la columna FK del hijo: muy recomendado. Acelera:
- Validaciones al insertar/actualizar.
ON DELETE/UPDATE CASCADE.JOINsfrecuentes entre padre e hijo.
Ejemplo:
CREATE INDEX ix_pedidos_cliente_id ON pedidos(cliente_id);
Sin ese índice, las operaciones de borrado/actualización del padre pueden disparar escaneos costosos en la tabla hija.
Errores y trampas frecuentes
- Tipos incompatibles
La FK debe tener mismo tipo (y longitud/collation idénticos para texto) que la columna referenciada. Si no, error. - Insertar hijos antes que el padre
No podés crear un registro hijo que apunte a un padre que aún no existe (salvo restricciones diferibles).
Solución: inserta primero el padre o usa transacciones con constraintsDEFERRABLE(cuando el motor lo soporte). - Borrado masivo con CASCADE inesperado
UnDELETEen el padre conON DELETE CASCADEpuede borrar miles de hijos.
Solución: entender el grafo de dependencias y auditar antes de ejecutar. - Falsos huérfanos por
SET NULL
Si la lógica del negocio no contempla hijos “desenganchados”,SET NULLpuede generar filas inútiles.
Solución: preferirRESTRICToCASCADEsegún el caso. - Deshabilitar FKs “por performance”
Desactivar validaciones para cargas masivas puede introducir datos corruptos que luego cuestan más. Si lo hacés, validá después. - Circularidad de referencias
Tablas que se referencian mutuamente complican el orden de inserción. Soluciones:
- Permitir
NULLtemporalmente en la FK. - Usar restricciones diferibles.
- Insertar en varias fases.
- Migraciones sin nombre de constraint
Dejar que el motor nombre la FK puede dificultar alter/drop posteriores. Mejor nombrarlas siempre.
Buenas prácticas
- Nombrado consistente:
fk_<tabla_hija>_<tabla_padre>. - Índices en las FKs del lado hijo.
- Elegir la política ON DELETE/UPDATE acorde al negocio (no “a ciegas”).
- Evitar FKs sobre columnas volátiles (cambian con frecuencia).
- Usar transacciones para operaciones masivas que afecten padre/hijos.
- Documentar el diagrama (ERD) y el sentido de cada relación.
- Pruebas de integridad: queries de auditoría para detectar huérfanos (si se deshabilitaron FKs temporalmente).
Auditoría: detectar huérfanos (cuando no hay FK o se desactivó)
SELECT p.id, p.cliente_id
FROM pedidos p
LEFT JOIN clientes c ON c.id = p.cliente_id
WHERE c.id IS NULL;
Si retorna filas, tenés pedidos que apuntan a clientes inexistentes: deberían corregirse o definirse FKs para prevenirlo.
Preguntas frecuentes (FAQ)
1) ¿La columna referenciada debe ser PRIMARY KEY?
Puede ser PRIMARY KEY o cualquier UNIQUE. Lo importante es que sea única y no nula.
2) ¿Se puede tener una FK a la misma tabla?
Sí (self-reference), típico para jerarquías como empleados/jefes o categorías/subcategorías.
3) ¿Puedo tener FKs compuestas?
Sí. Deben coincidir todas las columnas (orden y tipos) con la clave compuesta referenciada.
4) ¿Qué diferencia hay entre RESTRICT y NO ACTION?
En la práctica, suelen comportarse igual (bloquean la operación si hay hijos). Detalles finos dependen del SGBD.
5) ¿Conviene CASCADE?
Depende del negocio. CASCADE reduce código pero puede borrar en cadena. Si la eliminación física no es deseable, considerá soft delete (columna activo/eliminado_en) y mantené RESTRICT.
6) ¿Las FKs afectan la performance?
Sí, validan en cada INSERT/UPDATE/DELETE. Con índices adecuados, el impacto es bajo y la calidad de datos compensa con creces.
Conclusión
Las FOREIGN KEY son la base de la integridad referencial en modelos relacionales. Definirlas correctamente evita datos huérfanos, mantiene coherencia entre tablas y simplifica la lógica en la capa de aplicación.
Elegir bien las acciones ON DELETE/UPDATE, indexar las columnas involucradas y nombrar las restricciones de forma consistente son claves para un esquema robusto y mantenible.
Entiende llaves foráneas y referencias en Claves y restricciones y consulta el Glosario SQL completo.