En 1985, el matemático e informático Edgar F. Codd publicó trece reglas —numeradas de 0 a 12— para responder a una pregunta simple: ¿qué hace que un sistema de bases de datos sea verdaderamente "relacional"? Esta presentación recorre cada regla, su fundamento y cómo se traduce en SQL.
Edgar F. Codd · IBM Research · publicadas en 1985
Para que un sistema pueda llamarse "sistema de gestión de bases de datos relacional" (SGBDR), debe ser capaz de administrar sus bases de datos exclusivamente a través de sus capacidades relacionales, sin importar qué otras interfaces adicionales ofrezca.
Esta regla precede a las demás doce: es el criterio de admisión. Todo lo que la aplicación necesita debe poder lograrse a través del modelo relacional, no como un añadido opcional.
-- El motor relacional gestiona TODO: estructura, datos e integridad
CREATE TABLE clientes (
id INTEGER PRIMARY KEY,
nombre VARCHAR(100) NOT NULL
);
-- Toda operación pasa por el modelo relacional, sin atajos externos
SELECT * FROM clientes;
El dato fluye siempre de la aplicación al motor y de ahí al almacenamiento — en bucle continuo, nunca al revés.
Toda la información de la base de datos —datos de usuario o metadatos— debe representarse explícitamente a nivel lógico de una única forma: como valores dentro de tablas (filas y columnas). No existen estructuras ocultas como punteros visibles al usuario.
-- Toda la información se representa como valores dentro de tablas
SELECT nombre, email
FROM clientes
WHERE id = 42;
Un cursor recorre cada fila en bucle: toda la información vive dentro de la misma tabla, sin excepciones.
Cada dato atómico debe ser accesible de forma garantizada mediante la combinación de nombre de tabla + valor de clave primaria + nombre de columna. No se necesita ningún otro mecanismo de navegación para llegar a un valor específico.
-- Acceso garantizado: tabla + clave primaria + columna
SELECT salario
FROM empleados
WHERE id_empleado = 1024;
La clave primaria y la columna viajan en bucle hacia la misma celda: el punto exacto donde vive el dato.
El sistema debe soportar NULL como representación uniforme de información faltante o no aplicable, distinta de la cadena vacía y del número cero, e independiente del tipo de dato de la columna.
-- NULL es distinto de 0 y de una cadena vacía
CREATE TABLE pedidos (
id INTEGER PRIMARY KEY,
fecha_entrega DATE, -- NULL: fecha aún desconocida
total DECIMAL(10,2) NOT NULL DEFAULT 0
);
Un foco recorre las tres tarjetas en bucle: cada una guarda un significado distinto que nunca se mezcla.
La descripción de la base de datos (metadata: tablas, columnas, tipos, restricciones) se almacena también como tablas, y los usuarios autorizados pueden consultarla con el mismo lenguaje que usan para consultar sus propios datos.
-- El catálogo se consulta como cualquier tabla del usuario
SELECT table_name, column_name, data_type
FROM information_schema.columns
WHERE table_name = 'clientes';
Cada fila del catálogo se ilumina junto a su tabla real, en un bucle continuo de sincronía.
El sistema debe ofrecer al menos un lenguaje con sintaxis bien definida que cubra: definición de datos (DDL), definición de vistas, manipulación de datos (DML), restricciones de integridad, autorización (DCL) y control de transacciones. SQL cumple ese rol.
-- Un solo lenguaje cubre DDL, DML, DCL, integridad y transacciones
BEGIN TRANSACTION;
CREATE TABLE cuentas (
id INTEGER PRIMARY KEY,
saldo DECIMAL(10,2) NOT NULL
);
INSERT INTO cuentas VALUES (1, 1000.00);
GRANT SELECT ON cuentas TO auditor;
COMMIT;
Un mismo punto de datos recorre en bucle las cinco responsabilidades: todas caben en un único lenguaje.
Toda vista que sea teóricamente actualizable debe poder actualizarse a través del sistema. Los cambios sobre una vista actualizable deben reflejarse en las tablas base, y viceversa.
-- Una vista actualizable refleja cambios en la tabla base
CREATE VIEW empleados_activos AS
SELECT id, nombre, departamento
FROM empleados
WHERE activo = TRUE;
UPDATE empleados_activos
SET departamento = 'Ventas'
WHERE id = 7;
Los cambios viajan en ambos sentidos, sin parar: la vista y su tabla base siempre están sincronizadas.
El sistema debe soportar inserción, actualización y borrado como operaciones de conjunto: una sola sentencia puede afectar cero, una o muchas filas a la vez, no solo la lectura (SELECT).
-- Una sola sentencia opera sobre todo el conjunto de filas
UPDATE productos
SET precio = precio * 1.10
WHERE categoria = 'electronica';
A la izquierda todas las filas laten a la vez; a la derecha se procesan una por una, en cascada lenta.
Las aplicaciones deben seguir funcionando sin cambios cuando se modifica el almacenamiento físico o los métodos de acceso: agregar un índice o reorganizar archivos no debe romper ni una sola consulta existente.
-- Cambiar el almacenamiento no afecta las consultas existentes
CREATE INDEX idx_clientes_email ON clientes(email);
-- La aplicación sigue escribiendo exactamente la misma consulta:
SELECT * FROM clientes WHERE email = 'ana@ejemplo.com';
El almacenamiento alterna su estructura interna en bucle; la aplicación de arriba jamás lo nota.
Las aplicaciones deben permanecer lógicamente intactas ante cambios estructurales que preserven la información, como dividir una tabla en dos. Una vista de compatibilidad absorbe el cambio sin tocar el código existente.
-- Dividir una tabla no debe romper las consultas existentes
CREATE VIEW clientes AS
SELECT c.id, c.nombre, d.email
FROM clientes_base c
JOIN datos_contacto d ON d.cliente_id = c.id;
Los datos fluyen constantemente entre ambas tablas nuevas y la vista que finge ser el esquema de siempre.
Las restricciones de integridad deben poder definirse en el sublenguaje relacional y almacenarse en el catálogo, no repartirse como validaciones ad-hoc dentro de cada aplicación cliente.
-- La integridad vive en la base de datos, no en cada aplicación
ALTER TABLE cuentas
ADD CONSTRAINT chk_saldo_positivo CHECK (saldo >= 0);
ALTER TABLE pedidos
ADD CONSTRAINT fk_cliente
FOREIGN KEY (cliente_id) REFERENCES clientes(id);
A la izquierda el fallo salta de app en app; a la derecha una única puerta filtra a todas por igual, siempre.
El lenguaje de manipulación de datos debe verse y comportarse igual sin importar si los datos están centralizados o distribuidos en varios nodos: el motor relacional oculta esa complejidad al usuario final.
-- El lenguaje es el mismo sin importar dónde residan los datos
SELECT c.nombre, p.total
FROM clientes c
JOIN pedidos_sucursal_norte p ON p.cliente_id = c.id
WHERE p.total > 500;
Un flujo cíclico visita cada nodo por turnos: la aplicación solo le habla al motor, nunca directamente a los nodos.
Si el sistema ofrece una interfaz de bajo nivel (registro a registro), esa vía no puede usarse para saltarse las reglas de integridad ni las restricciones expresadas en el lenguaje relacional de alto nivel.
-- El acceso de bajo nivel no puede evadir las reglas de integridad
INSERT INTO cuentas (id, saldo) VALUES (99, -50);
-- ERROR: viola CHECK (saldo >= 0), incluso a bajo nivel
El camino directo se intenta una y otra vez, y una y otra vez rebota antes de tocar el almacenamiento.