- PRIMARY KEY
- FOREIGN KEY
- CHECK
- UNIQUE
- NOT NULL y DEFAULT
Clave primaria (PRIMARY KEY)
La clave primaria identifica de forma única cada fila de una tabla. Internamente, PostgreSQL crea un índiceUNIQUE sobre las columnas que la componen e impone NOT NULL en todas ellas. Puede ser simple (una columna) o compuesta (varias columnas).Clave primaria simple:CREATE TABLE IF NOT EXISTS table1 (
a VARCHAR(1),
b VARCHAR(1),
CONSTRAINT pk_table1 PRIMARY KEY (a)
);
INSERT INTO table1 (a, b) VALUES ('a', 'b');
table1, la columna a es la clave primaria. Intentar insertar dos filas con el mismo valor en a generará un error.Clave primaria compuesta:CREATE TABLE IF NOT EXISTS table3 (
c VARCHAR(1),
d INT,
e VARCHAR(1),
CONSTRAINT pk_table3 PRIMARY KEY (c, d)
);
INSERT INTO table3 (c, d, e) VALUES ('x', 1, 'w'), ('x', 2, 'z');
table3, la clave primaria es el par (c, d). Observa que el valor 'x' en la columna c se repite en ambas filas: eso es válido porque la unicidad se evalúa sobre la combinación de c y d, no sobre cada columna por separado.Prueba de violación:-- Esto falla: (c='x', d=1) ya existe
INSERT INTO table3 (c, d, e) VALUES ('x', 1, 'duplicado');
Nombrar las restricciones explícitamente (usando
CONSTRAINT nombre_restriccion) facilita enormemente la depuración: los mensajes de error mostrarán el nombre de la restricción violada en lugar de un identificador generado automáticamente.Clave foránea (FOREIGN KEY)
Una clave foránea establece un vínculo entre una columna (o conjunto de columnas) de una tabla hija y la clave primaria (o única) de una tabla padre. PostgreSQL verifica que cada valor insertado en la columna foránea exista en la tabla referenciada.FK simple:-- table2 referencia tanto a table1 (FK simple) como a table3 (FK compuesta)
CREATE TABLE IF NOT EXISTS table2 (
a VARCHAR(1),
c VARCHAR(1),
d INT,
CONSTRAINT pk_table2 PRIMARY KEY (a, c, d),
CONSTRAINT fk_a_t2 FOREIGN KEY (a) REFERENCES table1 (a),
CONSTRAINT fk_c_t2 FOREIGN KEY (c, d) REFERENCES table3 (c, d)
);
INSERT INTO table2 (a, c, d) VALUES ('a', 'x', 1), ('a', 'x', 2);
fk_c_t2 referencia las dos columnas (c, d) de table3. Ambas columnas deben coincidir simultáneamente con una fila existente en la tabla padre.Prueba de violación:-- Esto falla: 'z' no existe en table1.a
INSERT INTO table2 (a, c, d) VALUES ('z', 'x', 1);
-- Esto falla: ('x', 99) no existe en table3.(c, d)
INSERT INTO table2 (a, c, d) VALUES ('a', 'x', 99);
Comportamientos ON DELETE / ON UPDATE
Cuando se elimina o actualiza una fila en la tabla padre, PostgreSQL puede reaccionar de distintas formas dependiendo de la cláusula declarada en la FK:| Comportamiento | ON DELETE | ON UPDATE | Descripción |
|---|---|---|---|
NO ACTION | ✅ | ✅ | Error diferido al final de la transacción (comportamiento por defecto) |
RESTRICT | ✅ | ✅ | Error inmediato, no permite eliminar/actualizar si hay hijos |
CASCADE | ✅ | ✅ | Propaga la operación a las filas hijas automáticamente |
SET NULL | ✅ | ✅ | Pone NULL en las columnas foráneas de las filas hijas |
SET DEFAULT | ✅ | ✅ | Asigna el valor por defecto de la columna foránea |
CREATE TABLE departamentos (
id INTEGER PRIMARY KEY,
nombre VARCHAR(50) NOT NULL
);
CREATE TABLE empleados (
id INTEGER PRIMARY KEY,
nombre VARCHAR(50) NOT NULL,
depto_id INTEGER,
CONSTRAINT fk_depto FOREIGN KEY (depto_id)
REFERENCES departamentos (id)
ON DELETE CASCADE
ON UPDATE CASCADE
);
CREATE TABLE empleados_nullable (
id INTEGER PRIMARY KEY,
nombre VARCHAR(50) NOT NULL,
depto_id INTEGER,
CONSTRAINT fk_depto_null FOREIGN KEY (depto_id)
REFERENCES departamentos (id)
ON DELETE SET NULL
);
depto_id en los empleados pasa a NULL en lugar de eliminar la fila.Usar
ON DELETE CASCADE puede tener consecuencias inesperadas en esquemas con cadenas largas de referencias. Una sola eliminación en la tabla raíz puede desencadenar una cascada que elimine cientos o miles de filas en múltiples tablas. Úsalo con intención y documenta claramente el comportamiento esperado.Restricción CHECK
Una restricciónCHECK define una expresión booleana que debe evaluarse como TRUE (o NULL) para que la operación sea aceptada. Es la forma de imponer reglas de negocio directamente en el esquema.CHECK sobre un valor de lista:ALTER TABLE alumnos
ADD CONSTRAINT ck_sexo CHECK (sexo IN ('H', 'M'));
ALTER TABLE cursos
ADD CONSTRAINT ck_minimo_alumnos_requeridos CHECK (max_alumnos >= 10);
ALTER TABLE cursos
ADD CONSTRAINT ck_minimo_num_horas CHECK (num_horas > 100);
CREATE TABLE productos (
id INTEGER PRIMARY KEY,
nombre VARCHAR(100) NOT NULL,
precio NUMERIC(10, 2),
descuento NUMERIC(5, 2),
CONSTRAINT ck_precio_positivo CHECK (precio > 0),
CONSTRAINT ck_descuento_valido CHECK (descuento >= 0 AND descuento < precio)
);
CREATE TABLE reservas (
id INTEGER PRIMARY KEY,
fecha_inicio DATE NOT NULL,
fecha_fin DATE NOT NULL,
CONSTRAINT ck_fechas_validas CHECK (fecha_fin > fecha_inicio)
);
Las restricciones
CHECK no se evalúan cuando el valor es NULL. Si una columna acepta nulos, NULL siempre pasa el CHECK. Si necesitas que un valor no sea nulo y cumpla la condición, combina CHECK con NOT NULL.Restricción UNIQUE
UNIQUE garantiza que no haya dos filas con el mismo valor (o combinación de valores) en las columnas especificadas. A diferencia de PRIMARY KEY, permite valores NULL (en PostgreSQL, dos NULL no se consideran iguales para efectos de UNIQUE).UNIQUE simple:CREATE TABLE usuarios (
id INTEGER PRIMARY KEY,
email VARCHAR(100) NOT NULL,
username VARCHAR(50),
CONSTRAINT uq_email UNIQUE (email),
CONSTRAINT uq_username UNIQUE (username)
);
CREATE TABLE horarios (
id INTEGER PRIMARY KEY,
aula VARCHAR(10) NOT NULL,
dia VARCHAR(10) NOT NULL,
hora_inicio TIME NOT NULL,
CONSTRAINT uq_aula_dia_hora UNIQUE (aula, dia, hora_inicio)
);
ALTER TABLE usuarios
ADD CONSTRAINT uq_email UNIQUE (email);
NOT NULL
NOT NULL impide que una columna almacene el valor especial NULL. Se puede definir en línea al declarar la columna o añadirse después con ALTER TABLE.En la definición de la tabla:CREATE TABLE contactos (
id INTEGER PRIMARY KEY,
nombre VARCHAR(50) NOT NULL,
email VARCHAR(100) NOT NULL,
telefono VARCHAR(20) -- acepta NULL
);
-- Primero asegúrate de que no haya NULLs en la columna
UPDATE contactos SET email = 'sin-email@ejemplo.com' WHERE email IS NULL;
-- Luego aplica la restricción
ALTER TABLE contactos ALTER COLUMN email SET NOT NULL;
ALTER TABLE contactos ALTER COLUMN email DROP NOT NULL;
DEFAULT
DEFAULT define el valor que PostgreSQL asigna automáticamente a una columna cuando no se especifica en el INSERT.CREATE TABLE tareas (
id INTEGER PRIMARY KEY,
titulo VARCHAR(100) NOT NULL,
estado VARCHAR(20) DEFAULT 'pendiente',
prioridad INTEGER DEFAULT 3,
creado TIMESTAMPTZ DEFAULT NOW(),
activo BOOLEAN DEFAULT TRUE
);
-- Solo especificamos título; el resto toma sus defaults
INSERT INTO tareas (id, titulo) VALUES (1, 'Revisar documentación');
SELECT * FROM tareas WHERE id = 1;
-- estado = 'pendiente', prioridad = 3, activo = true, creado = timestamp actual
ALTER TABLE tareas ALTER COLUMN estado SET DEFAULT 'en_progreso';
-- Eliminar el valor por defecto
ALTER TABLE tareas ALTER COLUMN estado DROP DEFAULT;