Claves Foráneas Faltantes
Las filas huérfanas y los fallos de integridad aplicados por la aplicación ocurren cuando las columnas *_id carecen de REFERENCES, lo que impide que la base de datos rechace relaciones imposibles.
Receta
Tarjeta de receta de referencia rápida, lista para copiar y pegar.
-- Detección de olor: columna _id sin FK
CREATE TABLE comments (
comment_id bigint PRIMARY KEY,
post_id bigint NOT NULL
);
-- Solución: añadir FK (después de limpiar huérfanas)
DELETE FROM comments c
WHERE NOT EXISTS (SELECT 1 FROM posts p WHERE p.post_id = c.post_id);
ALTER TABLE comments
ADD CONSTRAINT comments_post_id_fkey
FOREIGN KEY (post_id) REFERENCES posts (post_id) ON DELETE CASCADE
NOT VALID;
ALTER TABLE comments VALIDATE CONSTRAINT comments_post_id_fkey;Cuándo usar esto:
- Tablas nuevas con columnas con sufijo
*_id. - Limpieza post-incidente después de que la integridad solo de la aplicación fallara.
- Importaciones de datos desde CSV sin comprobaciones referenciales.
Ejemplo de Trabajo
BEGIN;
CREATE TABLE authors (author_id bigint PRIMARY KEY, name text NOT NULL);
CREATE TABLE posts (
post_id bigint PRIMARY KEY,
author_id bigint NOT NULL REFERENCES authors (author_id)
);
-- Tabla hija solo de aplicación (defecto)
CREATE TABLE comments_bad (
comment_id bigint PRIMARY KEY,
post_id bigint NOT NULL
);
INSERT INTO posts VALUES (1, 1);
INSERT INTO comments_bad VALUES (100, 1);
INSERT INTO comments_bad VALUES (101, 999); -- huérfana - no existe el post 999
-- Detección
SELECT comment_id, post_id FROM comments_bad c
WHERE NOT EXISTS (SELECT 1 FROM posts p WHERE p.post_id = c.post_id);
-- Ruta de remediación
DELETE FROM comments_bad WHERE post_id = 999;
ALTER TABLE comments_bad
ADD CONSTRAINT comments_bad_post_id_fkey
FOREIGN KEY (post_id) REFERENCES posts (post_id) ON DELETE CASCADE;
-- Demostrar aplicación
INSERT INTO comments_bad VALUES (102, 888); -- ERROR: viola la FK
COMMIT;Lo que esto demuestra:
- Se insertó
post_id = 999huérfano sin error antes de la FK. - La consulta de huérfanas encuentra violaciones de integridad en bloque.
- La FK bloquea nuevas huérfanas en el límite de la base de datos.
Inmersión Profunda
Cómo Funciona
- Las restricciones de FK verifican la existencia del padre en
INSERT/UPDATEdel hijo. ON DELETEdefine el comportamiento de eliminación del padre (CASCADE,RESTRICT,SET NULL).NOT VALIDañade la FK sin escanear toda la tabla inmediatamente; luegoVALIDATE CONSTRAINTescanea con un bloqueo más débil en flujos de trabajo de PG 18.- La validación de la aplicación omite el SQL de administración, las condiciones de carrera y las rutas de código alternativas.
Guía de ON DELETE
| Acción | Usar cuándo |
|---|---|
CASCADE | El hijo no tiene sentido sin el padre (comentarios sobre una publicación) |
RESTRICT | Evitar la eliminación del padre si existen hijos (líneas de factura) |
SET NULL | Relación opcional, conservar el historial del hijo |
NO ACTION | Por defecto - diferir la comprobación hasta el final de la transacción |
Notas SQL
-- Encontrar todas las columnas llamadas %_id sin FK (auditoría heurística)
SELECT
c.table_schema, c.table_name, c.column_name
FROM information_schema.columns c
LEFT JOIN information_schema.key_column_usage k
ON k.table_schema = c.table_schema
AND k.table_name = c.table_name
AND k.column_name = c.column_name
WHERE c.column_name LIKE '%\_id' ESCAPE '\'
AND c.table_schema = 'public';
-- Se requiere revisión manual - no todas las columnas _id son FKTrampas
- FK eliminada "por rendimiento": regresan las huérfanas, las uniones devuelven filas basura. Solución: indexar columnas FK; mantener la restricción.
- Eliminación lógica de padres sin política de FK: los hijos apuntan a publicaciones
eliminadas. Solución:RESTRICTo sincronizar la eliminación lógica en la aplicación con la vista activa. - Importación masiva deshabilita triggers/FKs:
COPYcarga huérfanas. Solución: cargar en staging, validar, insertar con FKs habilitadas. - Referencias entre bases de datos: FK imposible entre bases de datos. Solución: trabajo de validación asíncrono o misma base de datos.
- Asociaciones polimórficas:
commentable_id+commentable_typeno pueden ser una única FK. Solución: tablas separadas o restricciones de verificación por tipo.
Alternativas
| Alternativa | Usar cuándo | No usar cuándo |
|---|---|---|
| Restricciones FK | OLTP por defecto | Referencias entre fragmentos en tablas fragmentadas |
EXCLUDE / CHECK | Reglas complejas | Existencia simple padre-hijo |
| Solo aplicación | Prototipo desechable | Datos de ingresos en producción |
| Trabajo periódico de huérfanas | Remediación de deuda heredada | Greenfield - añadir FK inmediatamente |
Preguntas Frecuentes
¿Las claves foráneas ralentizan las escrituras?
Pequeño sobrecoste por inserción - usualmente dominado por el mantenimiento de índices que necesitas de todos modos. Las FKs faltantes cuestan más en datos erróneos y tiempo de incidentes.
¿Debo indexar cada columna FK?
Sí, para el rendimiento de uniones y cascadas. PostgreSQL no indexa automáticamente las columnas FK.
¿Cómo añado una FK a una tabla de mil millones de filas?
ADD CONSTRAINT ... NOT VALID y luego VALIDATE CONSTRAINT en una ventana de mantenimiento; limpiar huérfanas primero.
¿Pueden las FKs referenciar claves no primarias?
Sí - deben referenciar columnas UNIQUE o PRIMARY KEY. UNIQUE (tenant_id, project_id) soporta FKs compuestas.
¿Qué pasa con las FKs circulares?
Usar restricciones diferidas o insertar padres en la misma transacción con ordenamiento de marcadores de posición - raro, diseñar cuidadosamente.
¿Funcionan las FKs en tablas particionadas?
Sí en PostgreSQL 18 con padres/hijos particionados - planificar el diseño de claves incluyendo las columnas de partición.
¿Debo usar ON DELETE CASCADE en todas partes?
No - las cascadas pueden sorprender a los operadores que eliminan una fila padre y borran miles de hijos. Elegir explícitamente.
¿Cómo funcionan las FKs multi-inquilino?
Incluir tenant_id en las claves del hijo y del padre: FOREIGN KEY (tenant_id, document_id) REFERENCES documents (tenant_id, document_id).
¿Puedo confiar en las asociaciones ORM sin FK?
No - el ORM no puede proteger contra SQL fuera de la aplicación. La FK de la base de datos es el contrato de registro.
¿Cómo detecto FKs faltantes en CI?
Analizar migraciones para nuevas columnas *_id; requerir REFERENCES coincidentes o un comentario de exención explícito en la plantilla de PR.
Relacionado
- Modelado Entidad-Relación - diseño de relaciones
- Conceptos Básicos de Escenarios de Defectos - detección de huérfanas
- Claves Primarias y Foráneas - mecánica de FK
- Mejores Prácticas para Escenarios de Defectos - reglas de linting
Versiones de la Pila: Esta página se escribió para PostgreSQL 18.4 (estable 18, mantenimiento 17), pgvector 0.8+, PgBouncer 1.x, Patroni 3.x, y PostGIS 3.5+.