⏱️ Lectura: 13 min

Por defecto, SQLite permite guardar texto en una columna declarada como número, o insertar un valor con formato desconocido en cualquier campo. Desde la versión 3.37.0, publicada en noviembre de 2021, existe una alternativa que corrige ese comportamiento: las tablas STRICT, que obligan a SQLite a respetar el tipo de dato declarado en cada columna.

📑 En este artículo
  1. TL;DR
  2. Introducción
  3. Qué pasó: el argumento a favor de las tablas STRICT en SQLite
  4. Contexto e historia
  5. Datos y cifras
  6. Impacto y análisis
    1. Cómo probarlo en tu máquina
  7. Qué sigue
  8. Preguntas frecuentes
    1. ¿Qué versión de SQLite introdujo las tablas STRICT?
    2. ¿Se puede convertir una tabla existente a STRICT?
    3. ¿Qué tipos de datos acepta una columna en una tabla STRICT?
    4. ¿Insertar el texto ‘123’ en una columna INTEGER STRICT da error?
    5. ¿SQLite recomienda usar tablas STRICT por defecto?
    6. ¿Las tablas STRICT afectan el rendimiento de las consultas?
  9. Referencias

El desarrollador Evan Hahn retomó el tema en un artículo publicado en su blog personal, donde explica por qué prefiere usar tablas STRICT en todos sus proyectos y por qué, en su opinión, deberían ser el comportamiento por defecto de la base de datos. A continuación repasamos cómo funcionan las tablas STRICT en SQLite, qué problemas resuelven y en qué escenarios conviene evitarlas.

TL;DR

  • SQLite introdujo las tablas STRICT en la versión 3.37.0, publicada el 27 de noviembre de 2021.
  • Una tabla STRICT solo acepta las columnas INT, INTEGER, REAL, TEXT, BLOB o ANY; cualquier otro tipo genera error al crearla.
  • Sin STRICT, SQLite permite insertar texto en una columna INTEGER sin lanzar ningún error.
  • Los valores convertibles sin pérdida, como el string ‘123’ hacia INTEGER, sí se aceptan en tablas STRICT.
  • No existe un comando ALTER para volver STRICT una tabla ya existente: hay que recrearla y copiar los datos.
  • El tipo ANY conserva la flexibilidad original de SQLite incluso dentro de una tabla STRICT.
  • La documentación oficial de SQLite defiende el tipado flexible como una ventaja para casos como almacenes clave-valor.

Introducción

SQLite es, según su propia documentación, el motor de base de datos más desplegado del mundo: vive dentro de navegadores, sistemas operativos móviles, aplicaciones de escritorio y un número enorme de herramientas que ni siquiera sabemos que la usan. A diferencia de motores como PostgreSQL o MySQL, SQLite nació con un sistema de tipos dinámico: cada columna tiene un tipo declarado, pero eso funciona más como una sugerencia que como una regla estricta.

Esto se conoce como afinidad de tipo. Una columna declarada como INTEGER intentará convertir lo que se inserte a un número entero, pero si no puede, simplemente guarda el valor original sin quejarse. Para muchos desarrolladores acostumbrados a bases de datos con tipado rígido, este comportamiento resulta sorprendente, y es exactamente el problema que las tablas STRICT en SQLite buscan resolver.

Qué pasó: el argumento a favor de las tablas STRICT en SQLite

En su publicación, Hahn muestra con ejemplos concretos por qué el tipado flexible de SQLite puede convertirse en una fuente silenciosa de bugs. El ejemplo más simple: una tabla sin STRICT permite insertar la palabra “garbage” en una columna definida como INTEGER, y la operación se completa sin ningún error.

-- Sin STRICT: SQLite acepta cualquier cosa
CREATE TABLE productos (precio INTEGER);
INSERT INTO productos (precio) VALUES ('gratis');
-- Se inserta sin error, aunque 'gratis' no es un número

-- Con STRICT: SQLite valida el tipo
CREATE TABLE productos_strict (precio INTEGER) STRICT;
INSERT INTO productos_strict (precio) VALUES ('gratis');
-- Error: cannot store TEXT value in INTEGER column productos_strict.precio

Basta con agregar la palabra clave STRICT al final de la sentencia CREATE TABLE para activar esta validación. El cambio no requiere una extensión adicional ni una configuración especial: es parte del núcleo de SQLite desde la versión 3.37.0.

Hahn también señala un matiz importante: las tablas STRICT no rechazan cualquier conversión, solo las que pierden información. Si insertás el string ‘123’ en una columna INTEGER STRICT, SQLite lo acepta porque puede convertirlo sin pérdida de datos. Ese comportamiento es distinto del rechazo total que ofrecen otros motores con tipado rígido, y busca un punto intermedio entre seguridad y comodidad.

Contexto e historia

El sistema de tipos dinámico de SQLite no es un descuido: es una decisión de diseño documentada desde hace más de una década. La propia documentación oficial incluye una página que argumenta a favor del tipado flexible, donde el equipo de SQLite sostiene que permitir cualquier valor en cualquier columna facilita casos de uso como un almacén clave-valor genérico, o la importación de datos “sucios” desde un CSV sin perder información en el proceso.

Sin embargo, la comunidad de desarrolladores lleva años señalando que ese diseño también facilita errores difíciles de detectar. Bugs donde un identificador llega como texto en lugar de número, o donde una fecha se guarda con un formato distinto al esperado, pueden pasar inadvertidos durante meses porque SQLite no los rechaza. La versión 3.37.0 fue la respuesta del propio proyecto a esa tensión: en lugar de cambiar el comportamiento por defecto, lo que hubiera roto compatibilidad con años de bases de datos existentes, agregó una palabra clave opcional que activa la validación estricta sin afectar a nadie que no la use.

Este patrón, mantener el comportamiento histórico por defecto y ofrecer una variante estricta opcional, no es exclusivo de SQLite. Lenguajes como TypeScript ofrecen un modo estricto sobre JavaScript sin tipos; PHP fue incorporando la declaración de tipos estrictos de forma progresiva. SQLite sigue una lógica similar: flexibilidad por defecto, rigidez cuando el desarrollador la pide explícitamente.

La comparación con otros motores es reveladora. MySQL, por ejemplo, activa por defecto un modo strict (STRICT_TRANS_TABLES) desde la versión 5.7, publicada en 2015, precisamente para evitar que datos inválidos se guarden silenciosamente o se trunquen sin aviso. PostgreSQL, por su parte, siempre exigió tipos rígidos desde su diseño original. SQLite es, en ese sentido, el motor más popular que todavía mantiene la flexibilidad como comportamiento por defecto, y las tablas STRICT son su forma de acercarse, de manera opcional, a lo que sus competidores ya ofrecen de fábrica.

Datos y cifras

Algunos datos concretos sobre las tablas STRICT en SQLite:

  • Se introdujeron en la versión 3.37.0, publicada el 27 de noviembre de 2021.
  • Una tabla STRICT solo admite seis tipos de columna: INT, INTEGER, REAL, TEXT, BLOB y ANY.
  • Cualquier otro nombre de tipo, incluidos errores de tipeo como “DATETIME”, “JSON” o “UUID”, que SQLite nunca soportó nativamente, genera un error al crear la tabla si esta es STRICT.
  • Sin STRICT, esos mismos nombres de tipo se aceptan sin problema al crear la tabla, aunque SQLite los trate internamente según su afinidad de tipo, no según el nombre literal.
  • No existe una sentencia para alterar una tabla y volverla STRICT: convertir una tabla existente requiere crear una nueva tabla, copiar los datos y renombrarla.

Según la documentación oficial del proyecto, SQLite es “el motor de base de datos más ampliamente desplegado y utilizado del mundo”, presente en cada instalación de Android e iOS, en los navegadores más usados y en un número creciente de aplicaciones de escritorio. Esa escala es justamente lo que hace relevante cualquier cambio en su sistema de tipos: una decisión de diseño en SQLite afecta, de forma indirecta, a una porción enorme del software que se ejecuta todos los días.

Ilustración conceptual de tablas y tipos de datos en una base de datos
Las tablas STRICT en SQLite limitan cada columna a un tipo declarado.

Impacto y análisis

El principal impacto de usar tablas STRICT en SQLite es preventivo: convierte errores silenciosos en errores explícitos y tempranos. Un bug que antes solo se manifestaba semanas después, cuando alguien intentaba hacer una operación matemática sobre un valor que en realidad era texto, ahora se detecta en el momento exacto de la inserción o actualización.

graph LR
 A["Insertar valor en columna INTEGER"] --> B{"La tabla es STRICT"}
 B -- "No" --> C["Se guarda el valor tal cual, sin validar"]
 B -- "Sí" --> D{"El valor se puede convertir sin pérdida"}
 D -- "Sí" --> E["Se convierte al tipo declarado"]
 D -- "No" --> F["Error: cannot store TEXT value in INTEGER column"]

💡 Tip: si necesitas que una columna acepte cualquier tipo de dato dentro de una tabla STRICT, por ejemplo para un almacén de configuración genérico, usa el tipo ANY. Mantiene la flexibilidad original de SQLite en esa columna puntual, sin renunciar a la validación en el resto de la tabla.

CREATE TABLE ajustes (clave TEXT, valor ANY) STRICT;

INSERT INTO ajustes (clave, valor) VALUES ('timeout', 30);
INSERT INTO ajustes (clave, valor) VALUES ('modo', 'oscuro');
INSERT INTO ajustes (clave, valor) VALUES ('factor', 1.5);
-- Las tres inserciones funcionan porque la columna valor es ANY

La otra cara de la moneda es la migración. Como no existe una forma directa de convertir una tabla existente a STRICT, adoptar esta práctica en un proyecto en producción implica un proceso manual: crear la tabla nueva, copiar los datos y reemplazar la original. Si la tabla vieja contiene datos inválidos, por ejemplo texto en una columna que debería ser numérica, esa migración va a fallar hasta que los datos se limpien o se conviertan explícitamente.

⚠️ Ojo: migrar una tabla existente a STRICT puede exponer datos corruptos que llevaban años acumulándose sin que nadie los detectara. Antes de migrar en producción, conviene correr una consulta de validación sobre los datos actuales.

Esa fricción también genera una duda legítima: conviene mezclar tablas STRICT con tablas tradicionales en el mismo proyecto. Hahn reconoce que sí es posible adoptar STRICT solo para las tablas nuevas, pero advierte que esto genera inconsistencia, parte de la base de datos queda validada y parte no, lo cual puede ser más confuso que no tener validación en ningún lado. La recomendación más común entre quienes adoptan esta práctica es decidirlo como una regla de equipo desde el inicio del proyecto, no de forma parcial.

Para equipos que recién arrancan un proyecto con SQLite, la recomendación práctica que se desprende de esta discusión es simple: definir desde el primer commit si el proyecto va a usar tablas STRICT en toda su base de datos, documentarlo en las convenciones del repositorio, y aplicarlo de forma consistente en cada migración nueva. Esa disciplina inicial evita la mezcla de tablas validadas y no validadas que Hahn señala como el peor escenario posible.

Cómo probarlo en tu máquina

Para experimentar con tablas STRICT alcanza con tener instalado el cliente de línea de comandos sqlite3, que ya viene incluido en la mayoría de distribuciones Linux y en macOS, aunque suele ser una versión antigua.

# Windows (con winget)
winget install SQLite.SQLite

# macOS (con Homebrew, versión más reciente que la del sistema)
brew install sqlite

# Linux (Debian/Ubuntu)
sudo apt update && sudo apt install sqlite3

# Linux (Fedora)
sudo dnf install sqlite

Con el cliente instalado, alcanza con abrir una base de datos y ejecutar cualquiera de los ejemplos de este artículo. El comando de versión permite confirmar que la instalación sea 3.37.0 o superior antes de probar STRICT.

Terminal ejecutando comandos de sqlite3 sobre una base de datos
El cliente sqlite3 permite crear y probar tablas STRICT desde la terminal.

Qué sigue

El propio Hahn admite que SQLite podría, en teoría, adoptar STRICT como comportamiento por defecto en una futura versión mayor, aunque eso rompería la compatibilidad de millones de bases de datos existentes, algo que el proyecto ha evitado históricamente. Es más probable que el camino a corto plazo pase por las herramientas que se construyen sobre SQLite: frameworks, ORMs y librerías de acceso a datos que empiecen a generar tablas STRICT por defecto al crear un proyecto nuevo, dejando la opción no STRICT como algo que hay que pedir explícitamente.

Ese es, de hecho, el patrón que ya siguen varias herramientas modernas del ecosistema: generadores de esquema que activan validaciones estrictas por defecto y dejan la flexibilidad como una opción consciente, no como el estado inicial. Si esa tendencia se consolida, las tablas STRICT en SQLite podrían pasar de ser una recomendación de blogs individuales a una convención ampliamente adoptada en nuevos proyectos, sin que el propio motor tenga que cambiar su comportamiento por defecto.

📖 Resumen en Telegram: Ver resumen

Preguntas frecuentes

¿Qué versión de SQLite introdujo las tablas STRICT?

La versión 3.37.0, publicada el 27 de noviembre de 2021, agregó tanto la palabra clave STRICT como el tipo de dato ANY.

¿Se puede convertir una tabla existente a STRICT?

No de forma directa. SQLite no ofrece un comando ALTER TABLE para esto: hay que crear una tabla nueva con STRICT, copiar los datos desde la tabla original y luego eliminar y renombrar según corresponda.

¿Qué tipos de datos acepta una columna en una tabla STRICT?

Solo seis: INT, INTEGER, REAL, TEXT, BLOB y ANY. Cualquier otro nombre de tipo, incluidos errores de tipeo, genera un error al crear la tabla.

¿Insertar el texto ‘123’ en una columna INTEGER STRICT da error?

No. SQLite acepta esa conversión porque es posible hacerla sin perder información: el string ‘123’ se convierte al entero 123 automáticamente.

¿SQLite recomienda usar tablas STRICT por defecto?

No en su documentación oficial. El proyecto mantiene que el tipado flexible tiene usos legítimos, como almacenes clave-valor o la importación de datos heterogéneos, y deja STRICT como una opción explícita del desarrollador.

¿Las tablas STRICT afectan el rendimiento de las consultas?

La validación adicional ocurre solo al insertar o actualizar datos, no al leerlos, por lo que el impacto se limita a operaciones de escritura y es en general marginal frente al resto del trabajo que hace una inserción.

Referencias

📱 ¿Te gusta este contenido? Únete a nuestro canal de Telegram @programacion donde publicamos a diario lo más relevante de tecnología, IA y desarrollo. Resúmenes rápidos, contenido fresco todos los días.

Categorías: Programación

Andrés Morales

Desarrollador e investigador en inteligencia artificial. Escribe sobre modelos de lenguaje, frameworks, herramientas para devs y lanzamientos open source. Cubre papers de ML, ecosistema de startups tech y tendencias de programación.

0 Comentarios

Deja un comentario

Marcador de posición del avatar

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *

Este sitio usa Akismet para reducir el spam. Aprende cómo se procesan los datos de tus comentarios.