# Constraint
## Qué es un constraint
Un **constraint** (restricción) es una **regla que el SGBD hace cumplir automáticamente** para proteger la **integridad de los datos**.
Si una instrucción `INSERT`/`UPDATE` viola la regla, **la operación falla** y la base **no queda en un estado inválido**.
Tres grandes objetivos que cubren los constraints:
- **Dominio**: que cada columna tenga valores válidos (tipo, rango, obligatoriedad).
- **Entidad**: que cada fila sea identificable de forma única.
- **Referencia**: que las relaciones entre tablas se mantengan coherentes.
## Tipos de constraints (estándar SQL)
1. **NOT NULL** – prohíbe valores nulos en una columna.
2. **UNIQUE** – asegura que **no se repitan** valores (puede ser de **una o varias columnas**).
3. **PRIMARY KEY** – identifica de forma única a cada fila (implícitamente `NOT NULL` + `UNIQUE`).
4. **FOREIGN KEY** – obliga a que un valor exista **previamente** en la tabla referenciada (integridad referencial).
5. **CHECK** – valida una **condición booleana** (rango, patrón, consistencia simple).
> Nota de versiones: **CHECK** se **enfuerza** en **MySQL ≥ 8.0.16** y **MariaDB ≥ 10.2.x**. En versiones más antiguas se ignoraba o requería alternativas (triggers).
## Ejemplos rápidos (MariaDB/MySQL)
### A. Definir constraints al crear tablas
```sql
CREATE TABLE student (
student_id INT AUTO_INCREMENT,
student_code VARCHAR(20) NOT NULL, -- NOT NULL
email VARCHAR(255) UNIQUE, -- UNIQUE
PRIMARY KEY (student_id), -- PRIMARY KEY
CONSTRAINT uq_student_code UNIQUE (student_code), -- UNIQUE nombrado
CONSTRAINT chk_code_len CHECK (CHAR_LENGTH(student_code) >= 3) -- CHECK
) ENGINE=InnoDB;
CREATE TABLE course (
course_id INT AUTO_INCREMENT PRIMARY KEY,
code VARCHAR(20) NOT NULL UNIQUE,
credits TINYINT NOT NULL,
CONSTRAINT chk_credits CHECK (credits BETWEEN 1 AND 10)
) ENGINE=InnoDB;
CREATE TABLE enrollment (
student_id INT NOT NULL,
course_id INT NOT NULL,
enrolled_at DATETIME DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (student_id, course_id), -- UNIQUE del par
CONSTRAINT fk_enr_student FOREIGN KEY (student_id)
REFERENCES student(student_id) ON DELETE CASCADE,
CONSTRAINT fk_enr_course FOREIGN KEY (course_id)
REFERENCES course(course_id) ON DELETE RESTRICT
) ENGINE=InnoDB;
```
### B. Añadir o quitar constraints después (ALTER TABLE)
```sql
-- Agregar UNIQUE compuesto
ALTER TABLE student
ADD CONSTRAINT uq_student_name_email UNIQUE (full_name, email);
-- Agregar FOREIGN KEY
ALTER TABLE enrollment
ADD CONSTRAINT fk_enr_course FOREIGN KEY (course_id)
REFERENCES course(course_id) ON DELETE RESTRICT;
-- Quitar una FOREIGN KEY (usa su nombre)
ALTER TABLE enrollment DROP FOREIGN KEY fk_enr_course;
-- Quitar un UNIQUE en MySQL/MariaDB se hace como índice:
ALTER TABLE student DROP INDEX uq_student_name_email;
-- Quitar un CHECK (MySQL 8+)
ALTER TABLE course DROP CHECK chk_credits;
```
## ¿Por qué usar constraints y no sólo validación en la app?
- **Consistencia centralizada**: protegen los datos aunque haya **múltiples aplicaciones** o usuarios conectados.
- **Atómicas**: se aplican dentro de la transacción; no hay “ventanas” para estados inválidos.
- **Performance**: `PRIMARY KEY` y `UNIQUE` crean índices útiles; las **FK** suelen beneficiarse de índices en las columnas referenciadas.
## Buenas prácticas
- **Nombra tus constraints** (`fk_enr_course`, `chk_credits`): facilita depurar y alterar.
- **Usa `NOT NULL` por defecto** salvo que _nulo_ tenga un significado claro.
- **Prefiere `UNIQUE` multicolumna** para reglas como “no hay dos matrículas del mismo estudiante en el mismo curso”.
- **Define `ON DELETE/UPDATE`** en FKs de acuerdo al negocio (`CASCADE`, `RESTRICT`, `SET NULL`).
- **CHECK para reglas de dominio** (rangos, formatos). Para reglas complejas que cruzan varias filas/ tablas, evalúa **triggers**.
- **Índices para FKs**: añade un índice en la columna que referencia; mejora `JOIN` y borrados/actualizaciones en cascada.
## Errores típicos
- Confiar sólo en la app: otro cliente puede saltarse la validación.
- `CHECK` en versiones antiguas de MySQL/MariaDB → no se aplicaba (ver tu versión).
- Falta de índices en FKs → `JOIN` lentos y cascadas costosas.
- `PRIMARY KEY` no estable (p. ej., usar un dato que cambia). Mejor **surrogate key** (`AUTO_INCREMENT`) y **UNIQUE** para la llave natural.