CHECK
La restricción CHECK valida que los valores insertados o actualizados cumplan una condición específica. Puedes definir reglas como "el precio debe ser positivo", "la edad debe estar entre 18 y 65" o "la fecha de fin debe ser posterior a la fecha de inicio". MySQL 8.0.16 introdujo el soporte completo para CHECK (versiones anteriores lo parseaban pero no lo aplicaban).
Sintaxis
Como restricción de columna:
CREATE TABLE nombre_tabla (
columna tipo_de_dato CHECK (condicion)
);Como restricción de tabla con nombre:
CREATE TABLE nombre_tabla (
columna tipo_de_dato,
CONSTRAINT nombre_check CHECK (condicion)
);Ejemplo básico
CREATE TABLE productos_demo (
id INT AUTO_INCREMENT PRIMARY KEY,
nombre VARCHAR(200) NOT NULL,
precio DECIMAL(10, 2) NOT NULL CHECK (precio > 0),
stock INT NOT NULL CHECK (stock >= 0),
descuento DECIMAL(5, 2) DEFAULT 0 CHECK (descuento BETWEEN 0 AND 100)
);Tres restricciones CHECK: el precio debe ser positivo, el stock no puede ser negativo, y el descuento debe estar entre 0 y 100.
-- Funciona
INSERT INTO productos_demo (nombre, precio, stock) VALUES ('Laptop', 999.99, 10);
-- Falla: precio negativo
INSERT INTO productos_demo (nombre, precio, stock) VALUES ('Test', -50, 5);ERROR 3819 (HY000): Check constraint 'productos_demo_chk_1' is violated.
-- Falla: stock negativo
INSERT INTO productos_demo (nombre, precio, stock) VALUES ('Test', 50, -1);ERROR 3819 (HY000): Check constraint 'productos_demo_chk_2' is violated.
-- Falla: descuento mayor que 100
INSERT INTO productos_demo (nombre, precio, stock, descuento) VALUES ('Test', 50, 5, 150);ERROR 3819 (HY000): Check constraint 'productos_demo_chk_3' is violated.
CHECK en tienda_mysql
Nuestra base de datos usa CHECK en la tabla resenas para validar la puntuación:
-- La tabla resenas tiene: CHECK (puntuacion BETWEEN 1 AND 5)
INSERT INTO resenas (producto_id, cliente_id, puntuacion, comentario)
VALUES (1, 2, 6, 'Excelente');ERROR 3819 (HY000): Check constraint 'resenas_chk_1' is violated.
La puntuación 6 viola la restricción porque está fuera del rango 1-5.
Nombrar restricciones CHECK
Dar nombres descriptivos a las restricciones mejora los mensajes de error:
CREATE TABLE empleados_demo (
id INT AUTO_INCREMENT PRIMARY KEY,
nombre VARCHAR(100) NOT NULL,
salario DECIMAL(10, 2) NOT NULL,
edad INT,
email VARCHAR(150),
CONSTRAINT chk_salario_positivo CHECK (salario > 0),
CONSTRAINT chk_edad_valida CHECK (edad BETWEEN 18 AND 70),
CONSTRAINT chk_email_formato CHECK (email LIKE '%@%.%')
);Ahora los errores son más informativos:
INSERT INTO empleados_demo (nombre, salario, edad) VALUES ('Ana', -100, 25);ERROR 3819 (HY000): Check constraint 'chk_salario_positivo' is violated.
El nombre chk_salario_positivo indica claramente qué regla se violó.
CHECK con múltiples columnas
Las restricciones CHECK pueden referenciar varias columnas de la misma tabla:
CREATE TABLE eventos (
id INT AUTO_INCREMENT PRIMARY KEY,
nombre VARCHAR(200) NOT NULL,
fecha_inicio DATE NOT NULL,
fecha_fin DATE NOT NULL,
precio_normal DECIMAL(10, 2) NOT NULL,
precio_vip DECIMAL(10, 2) NOT NULL,
CONSTRAINT chk_fechas CHECK (fecha_fin >= fecha_inicio),
CONSTRAINT chk_precios CHECK (precio_vip >= precio_normal),
CONSTRAINT chk_precios_positivos CHECK (precio_normal > 0)
);-- Funciona: fecha_fin posterior y precio VIP mayor
INSERT INTO eventos (nombre, fecha_inicio, fecha_fin, precio_normal, precio_vip)
VALUES ('Conferencia MySQL', '2025-06-01', '2025-06-03', 50.00, 100.00);
-- Falla: fecha_fin anterior a fecha_inicio
INSERT INTO eventos (nombre, fecha_inicio, fecha_fin, precio_normal, precio_vip)
VALUES ('Evento inválido', '2025-06-03', '2025-06-01', 50.00, 100.00);ERROR 3819 (HY000): Check constraint 'chk_fechas' is violated.
CHECK en UPDATE
Las restricciones CHECK también se aplican en actualizaciones:
UPDATE productos_demo SET precio = -10 WHERE id = 1;ERROR 3819 (HY000): Check constraint 'productos_demo_chk_1' is violated.
UPDATE productos_demo SET stock = stock - 100 WHERE id = 1;ERROR 3819 (HY000): Check constraint 'productos_demo_chk_2' is violated.
Si el stock actual es 10 y restas 100, el resultado (-90) viola la restricción stock >= 0.
CHECK con ENUM
Puedes usar CHECK para validar valores de columnas que no son ENUM:
CREATE TABLE pedidos_demo (
id INT AUTO_INCREMENT PRIMARY KEY,
estado VARCHAR(20) NOT NULL DEFAULT 'pendiente',
CONSTRAINT chk_estado CHECK (estado IN ('pendiente', 'procesando', 'enviado', 'entregado', 'cancelado'))
);Esto ofrece la misma validación que un ENUM, pero con más flexibilidad: puedes añadir o quitar valores modificando la restricción sin reconstruir la tabla.
Añadir CHECK a tabla existente
ALTER TABLE productos_demo
ADD CONSTRAINT chk_nombre_no_vacio CHECK (LENGTH(nombre) > 0);Si alguna fila existente viola la nueva restricción, la operación falla. Primero verifica los datos:
SELECT id, nombre FROM productos_demo WHERE LENGTH(nombre) = 0;Eliminar CHECK
ALTER TABLE productos_demo
DROP CHECK chk_nombre_no_vacio;Si no nombraste la restricción, MySQL le asigna un nombre automático (como productos_demo_chk_1). Puedes consultarlo:
SELECT CONSTRAINT_NAME, CHECK_CLAUSE
FROM information_schema.CHECK_CONSTRAINTS
WHERE CONSTRAINT_SCHEMA = DATABASE()
AND TABLE_NAME = 'productos_demo';CHECK y NULL
Las restricciones CHECK tratan NULL de forma especial. Si la expresión evalúa a NULL (no a TRUE ni a FALSE), la restricción se considera satisfecha:
INSERT INTO empleados_demo (nombre, salario, edad)
VALUES ('Luis', 35000, NULL);Query OK, 1 row affected (0.01 sec)
La inserción funciona aunque edad es NULL. La expresión NULL BETWEEN 18 AND 70 evalúa a NULL, que no es FALSE, así que la restricción no la rechaza. Si quieres prohibir NULL, combina CHECK con NOT NULL.
Limitaciones
Las restricciones CHECK en MySQL tienen algunas limitaciones. No pueden referenciar otras tablas (solo la tabla actual). No pueden contener subconsultas. No pueden usar funciones no deterministas como RAND() o NOW() (aunque CURRENT_TIMESTAMP funciona en algunas versiones). No pueden referenciar columnas generadas.
-- Esto NO funciona: referencia a otra tabla
-- CHECK (departamento_id IN (SELECT id FROM departamentos))
-- Esto NO funciona: función no determinista
-- CHECK (creado_en > NOW())Para validaciones que involucren otras tablas, usa triggers o lógica de aplicación.
Desactivar temporalmente
A diferencia de las claves foráneas, MySQL no tiene una variable para desactivar CHECK globalmente. Sin embargo, puedes eliminar y recrear la restricción para importaciones masivas:
ALTER TABLE productos_demo DROP CHECK chk_salario_positivo;
-- Importar datos...
ALTER TABLE productos_demo ADD CONSTRAINT chk_salario_positivo CHECK (salario > 0);Limpieza
DROP TABLE IF EXISTS productos_demo;
DROP TABLE IF EXISTS empleados_demo;
DROP TABLE IF EXISTS eventos;
DROP TABLE IF EXISTS pedidos_demo;En el siguiente artículo veremos AUTO_INCREMENT, que genera valores secuenciales automáticamente para claves primarias.
Escrito por Eduardo Lázaro
