Menu

Restricciones CHECK en SQLite: validar datos en la tabla

Cómo usar restricciones CHECK en SQLite para validar valores: comprobaciones de una o varias columnas, restricciones con nombre y los detalles que conviene tener en cuenta.

Esta página incluye editores ejecutables: edita, ejecuta y ve el resultado al instante.

Una restricción CHECK es una regla que toda fila debe cumplir

Una restricción CHECK en SQLite es una expresión booleana que asocias a una tabla. SQLite la evalúa en cada INSERT y UPDATE, y si la expresión devuelve falso, la operación se aborta. Es la forma de incrustar una regla de negocio directamente en el esquema: "el precio no puede ser negativo", "el estado tiene que ser uno de estos tres valores", y cosas por el estilo.

Las dos primeras filas entran sin problema. La tercera dispara CHECK constraint failed y se rechaza: la tabla ni se entera. La restricción aplica la regla a cualquiera que escriba, ya sea tu aplicación, un script de migración o alguien trasteando desde la CLI.

CHECK a nivel de columna vs a nivel de tabla

Una restricción CHECK en SQLite se puede declarar en dos sitios: justo después de la definición de una columna (nivel de columna) o al final, después de todas las columnas (nivel de tabla). El comportamiento es idéntico; lo que cambia es cuál se lee de forma más natural según el caso.

La primera reserva se inserta sin problema. La segunda falla, porque la fecha de fin es anterior a la de inicio. Las reglas que afectan a una sola columna se leen mejor a nivel de columna; cuando comparas dos o más columnas, conviene declararlas a nivel de tabla.

Restringir valores a una lista concreta

Un uso muy habitual de la restricción CHECK en SQLite es obligar a que una columna solo acepte uno de varios valores fijos. Como SQLite no tiene un tipo enum nativo, el patrón típico es usar CHECK ... IN (...):

La tercera fila falla: 'pending' no está en la lista de valores permitidos. Si más adelante necesitas añadir un nuevo estado, vas a tener que reconstruir la tabla (lo veremos más abajo), así que piénsalo bien antes de cerrar la lista. Aun así, para vocabularios realmente fijos como nombres de roles o estados de pedidos, esta es justo la restricción que te conviene.

Cómo nombrar tus restricciones CHECK

Por defecto, una restricción es anónima. El mensaje de error se limita a decir "CHECK constraint failed" junto con la expresión, lo cual está bien cuando solo hay un CHECK en la tabla, pero se vuelve un lío cuando tienes cinco. Para ponerle nombre, usa CONSTRAINT:

Ahora el mensaje de error incluye el nombre de la restricción, así sabes al instante qué regla se violó. Ponerle nombre cuesta unos caracteres de más y se amortiza la primera vez que algo revienta en producción.

CHECK y NULL: el detalle que te puede pillar

Una restricción CHECK pasa cuando la expresión es verdadera o NULL. Solo falla cuando el resultado es explícitamente falso. Suena raro hasta que recuerdas que casi cualquier comparación con NULL devuelve NULL, no verdadero ni falso.

La fila con NULL entra sin problema, ya que NULL >= 0 no devuelve falso, sino NULL, así que la restricción CHECK no se rompe. Si lo que quieres es prohibir tanto los números negativos como los valores ausentes, combina NOT NULL con el CHECK:

Ahora el INSERT falla por la restricción NOT NULL antes incluso de que se evalúe el CHECK. Las dos restricciones se complementan: NOT NULL se encarga de la ausencia de valor y CHECK se encarga del formato.

Funciones integradas útiles dentro de un CHECK

Dentro de la expresión puedes usar casi todas las funciones integradas de SQLite. Estas son algunas que aparecen muy seguido:

Tres fallos: un formato de email incorrecto, un nombre de usuario demasiado corto y un código de país en minúsculas. LIKE cubre patrones simples; length(), upper(), lower() y la aritmética también valen. Eso sí, mantén la expresión determinista: usar algo como random() o current_timestamp dentro de un CHECK genera reglas que pueden cambiar de una fila a otra, y casi nunca es lo que buscas.

CHECK vs Trigger en SQLite

Tanto CHECK como los triggers pueden rechazar datos inválidos, y es habitual dudar cuál usar al empezar. La regla práctica:

  • CHECK cuando la regla solo depende de la fila que se está escribiendo. "Esta columna comparada con esa otra", "este valor dentro de un rango", "esta cadena cumple un patrón".
  • Trigger (en concreto un trigger BEFORE INSERT/UPDATE que llame a RAISE) cuando la regla depende de otras filas, otras tablas, o hace falta algo más elaborado que una sola expresión booleana.

CHECK es más rápido, más simple y queda a la vista en el esquema: cualquiera que lea el CREATE TABLE ve la regla. Tira de triggers solo cuando CHECK no llegue a expresar lo que necesitas.

No se puede eliminar un CHECK con ALTER

Aquí está la pega. SQLite no tiene ALTER TABLE ... DROP CONSTRAINT. Para eliminar o modificar un CHECK, hay que reconstruir la tabla:

BEGIN;

CREATE TABLE products_new (
    id    INTEGER PRIMARY KEY,
    name  TEXT NOT NULL,
    price REAL NOT NULL CHECK (price >= 0 AND price <= 1000000)
);

INSERT INTO products_new SELECT * FROM products;
DROP TABLE products;
ALTER TABLE products_new RENAME TO products;

COMMIT;

Envuelve todo dentro de una transacción para que, si algo falla a medio camino, la base de datos quede intacta. Si otras tablas tienen claves foráneas apuntando a la tabla que estás reconstruyendo, el baile se alarga: hay que desactivar foreign_keys, reconstruir, volver a activarlas y revalidar. Lo veremos más adelante, en el documento de migraciones del curso.

Siguiente paso: restricciones UNIQUE

CHECK valida la forma de los valores dentro de una misma fila. La siguiente restricción, UNIQUE, valida relaciones entre filas: garantiza que no haya dos filas con el mismo valor en una columna o en un conjunto de columnas. Eso es lo que viene a continuación.

Preguntas frecuentes

¿Qué es una restricción CHECK en SQLite?

Una restricción CHECK es una expresión booleana asociada a una tabla que toda fila debe cumplir. SQLite la evalúa en cada INSERT o UPDATE y rechaza el cambio si la expresión devuelve falso. Es la forma más sencilla de imponer una regla del estilo «el precio tiene que ser positivo» sin tener que escribir código en la aplicación.

¿Puede una restricción CHECK referenciar varias columnas en SQLite?

Sí, pero conviene declararla a nivel de tabla en lugar de asociarla a una sola columna. Por ejemplo, CHECK (start_date <= end_date) declarada después de la lista de columnas puede referenciar ambas. Las restricciones a nivel de columna también pueden referirse a otras columnas, pero la versión a nivel de tabla se lee mucho mejor cuando hay más de una columna implicada.

¿Por qué mi restricción CHECK no salta con valores NULL?

CHECK pasa cuando la expresión es verdadera o NULL: solo falla si es explícitamente falsa. Por eso CHECK (age >= 0) acepta un age con valor NULL, ya que NULL >= 0 da NULL, no falso. Si también quieres prohibir NULL, añade un NOT NULL junto al CHECK.

¿Se puede eliminar o modificar una restricción CHECK en SQLite?

Directamente no. SQLite no soporta ALTER TABLE ... DROP CONSTRAINT. Para cambiar un CHECK tienes dos opciones: editar sqlite_schema con PRAGMA writable_schema (avanzado y arriesgado) o reconstruir la tabla — crear una nueva con las restricciones que quieras, copiar los datos, borrar la antigua y renombrar. Si pones nombre a tus restricciones, el script de reconstrucción se lee mucho más fácil.

Coddy programming languages illustration

Aprende a programar con Coddy

COMENZAR