Menu

SQLite NOT NULL y DEFAULT: restricciones de columna

Cómo funcionan NOT NULL y DEFAULT en SQLite: qué obligan de verdad, el truco con CURRENT_TIMESTAMP y los problemas al añadirlos a tablas que ya existen.

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

Dos restricciones que valen su peso en oro

La mayoría de los bugs que nacen de un esquema descuidado se reducen a dos cosas: una columna que llega NULL cuando nadie lo esperaba, o una columna a la que le falta un valor que la aplicación daba por hecho. Las restricciones NOT NULL y DEFAULT en SQLite resuelven ambos casos, y casi no cuesta nada añadirlas.

Una columna es obligatoria y no tiene valor de respaldo. Las otras dos sí lo tienen. En el INSERT solo hizo falta indicar email; SQLite se encargó de rellenar el resto. Eso es toda la funcionalidad resumida en un ejemplo: el resto de esta página trata sobre los casos límite.

NOT NULL: rechaza NULL sin excepciones

La restricción NOT NULL en SQLite hace exactamente lo que promete. Cualquier intento de meter NULL en la columna falla, ya sea porque lo omites en un INSERT sin valor por defecto o porque escribes NULL de forma explícita:

El error dice:

Runtime error: NOT NULL constraint failed: posts.title

Pasa lo mismo si le mandas NULL directamente:

INSERT INTO posts (id, title) VALUES (1, NULL);
-- Error en tiempo de ejecución: NOT NULL constraint failed: posts.title

Ese es el contrato. Si una columna es obligatoria por lógica de negocio, márcala como NOT NULL y te quitas de encima toda una familia de bugs: ningún código de aplicación va a colar un NULL a espaldas de la base de datos.

DEFAULT: un valor por defecto en SQLite cuando quien inserta no lo proporciona

DEFAULT entra en juego únicamente cuando el INSERT ni siquiera menciona la columna. No te salva si pasas un NULL de forma explícita:

El primer INSERT se apoya en el valor por defecto. El segundo lo sobrescribe. Si hubieras escrito INSERT INTO tasks (title, status) VALUES ('x', NULL), te llevarías un error NOT NULL constraint failed: como nombraste la columna explícitamente, el DEFAULT ni siquiera entra en juego.

Quédate con este modelo mental: DEFAULT rellena las columnas que no mencionas. NOT NULL rechaza los nulos vengan de donde vengan. Son dos mecanismos independientes que encajan muy bien entre sí.

Los valores por defecto pueden ser expresiones

Lo habitual es usar un literal como valor por defecto (DEFAULT 0, DEFAULT '', DEFAULT 'pending'), pero SQLite también admite expresiones entre paréntesis. Así es como marcas las filas con su fecha de creación o generas un ID aleatorio:

Algunas cosas que conviene tener presentes:

  • La expresión se evalúa en cada inserción, no una sola vez al crear la tabla. Cada fila obtiene su propio timestamp y su propio token.
  • CURRENT_TIMESTAMP, CURRENT_DATE y CURRENT_TIME son las tres palabras clave especiales que no requieren paréntesis. Cualquier otra cosa sí los necesita.
  • La expresión no puede hacer referencia a otras columnas ni a subconsultas: tiene que ser autocontenida.

Si quieres que una columna sea opcional pero que se rellene automáticamente cuando esté presente, omite el NOT NULL y deja el valor por defecto. Si la quieres obligatoria y con sello automático, usa ambos.

DEFAULT NULL es válido (y a veces es justo lo que buscas)

Escribir DEFAULT NULL es equivalente a no poner ningún valor por defecto: la columna queda en NULL cuando no proporcionas un valor. Aun así, tiene sentido usarlo cuando quieres dejar explícito en el esquema que "sin valor" es el estado inicial deseado:

bio y avatar se comportan exactamente igual aquí. El DEFAULT NULL de bio funciona como un comentario en forma de código: le indica a cualquiera que lea el esquema que la ausencia de biografía es algo esperado, no un descuido.

Añadir NOT NULL a una tabla existente

Aquí es donde la cosa se pone peliaguda. El ALTER TABLE de SQLite es limitado a propósito: no puedes ejecutar ALTER COLUMN ... SET NOT NULL como harías en Postgres. Lo que puedes hacer depende de si la columna ya existe o no.

Si se trata de una columna nueva, ADD COLUMN ... NOT NULL sirve, pero tienes que proporcionar un valor por defecto. De lo contrario, las filas existentes quedarían con NULL en una columna NOT NULL, lo cual es imposible:

Prueba lo mismo sin el DEFAULT y verás que salta un error:

ALTER TABLE products ADD COLUMN sku TEXT NOT NULL;
-- Error en tiempo de ejecución: No se puede agregar una columna NOT NULL con valor por defecto NULL

Para una columna que ya existe, no hay forma de modificarla en el sitio. La receta habitual es el famoso baile de reconstrucción: creas una tabla nueva con la restricción que quieres, copias los datos, eliminas la vieja y renombras la nueva. Lo veremos en detalle en la página drop-and-alter-table — por ahora, ten presente que esta limitación existe y diseña tu esquema teniéndola en cuenta.

Una combinación realista

La mayoría de tablas en producción usan ambas restricciones juntas para reflejar "lo que la aplicación espera que se cumpla":

Si lees ese esquema de arriba abajo, puedes adivinar qué hace la aplicación sin ver una sola línea de código. customer es obligatorio y no tiene alternativa: quien hace la llamada tiene que saber para quién es el pedido. El importe, la moneda y el estado tienen valores por defecto razonables, así que el INSERT más simple ya produce una fila coherente. notes es opcional. created_at lo rellena la base de datos, que es el único sitio donde debería rellenarse.

Ese es el valor de estas restricciones de columna: convierten suposiciones en reglas que la propia base de datos hace cumplir.

Errores comunes

Una lista corta de cosas con las que mucha gente tropieza:

  • Pasar NULL explícitamente anula el DEFAULT. INSERT INTO t (col) VALUES (NULL) no usa el valor por defecto. La columna tiene que estar ausente de la lista de columnas.
  • Los defaults con expresión necesitan paréntesis. DEFAULT CURRENT_TIMESTAMP funciona (es una de las tres palabras clave especiales). DEFAULT lower(hex(randomblob(8))) no funciona; hay que envolverlo: DEFAULT (lower(hex(randomblob(8)))).
  • NOT NULL y la cadena vacía no son lo mismo. '' es un valor TEXT válido y no dispara la restricción. Si además quieres prohibir cadenas vacías, eso ya es trabajo de CHECK (siguiente página).
  • ADD COLUMN ... NOT NULL exige un DEFAULT que no sea NULL. Sin él, SQLite rechaza el cambio.

A continuación: restricciones CHECK

NOT NULL y DEFAULT cubren el "tiene que existir" y el "rellénalo si falta". Para la siguiente capa de validación — "tiene que ser positivo", "tiene que ser uno de estos valores", "la fecha de fin tiene que ser posterior a la de inicio" — SQLite ofrece las restricciones CHECK, que te permiten escribir expresiones booleanas arbitrarias que toda fila debe cumplir. De eso va la siguiente página.

Preguntas frecuentes

¿Cómo hago que una columna sea obligatoria en SQLite?

Añade NOT NULL en la definición de la columna: email TEXT NOT NULL. Cualquier INSERT o UPDATE que intente dejar esa columna como NULL falla con NOT NULL constraint failed. Si quieres un valor de respaldo cuando quien inserta no proporciona ninguno, combínalo con un DEFAULT.

¿Cómo funcionan los valores por defecto en SQLite?

DEFAULT <valor> le da a la columna un valor que se usa cuando un INSERT no especifica ninguno. El valor por defecto puede ser un literal (DEFAULT 0, DEFAULT 'pending'), NULL, o una expresión entre paréntesis como DEFAULT (CURRENT_TIMESTAMP) o DEFAULT (lower(hex(randomblob(8)))). Las expresiones se vuelven a evaluar en cada inserción.

¿Por qué SQLite me da 'NOT NULL constraint failed' al insertar?

Estás insertando una fila sin dar un valor para una columna NOT NULL que no tiene DEFAULT. Tienes tres opciones: incluir la columna en el INSERT, asignarle un DEFAULT o quitar la restricción. Pasar NULL de forma explícita también lo dispara: NOT NULL rechaza nulos venga de donde venga.

¿Puedo añadir NOT NULL a una columna que ya existe en SQLite?

Directamente no: en SQLite no existe ALTER TABLE ... ALTER COLUMN. Las dos vías son: añadir una columna nueva con NOT NULL DEFAULT <valor> (el DEFAULT es obligatorio para las filas existentes) o reconstruir la tabla — creas una nueva con la restricción, copias los datos, eliminas la antigua y renombras la nueva.

Coddy programming languages illustration

Aprende a programar con Coddy

COMENZAR