¿SERIAL o IDENTITY en PostgreSQL? El autoincremento que la mayoría de los esquemas elige mal
Cuando hay que generar primary key autoincrementales en PostgreSQL, elegir entre “identity” y tipos “serial” tiene consecuencias importantes para el diseño y el rendimiento de la base de datos. En este artículo vemos las diferencias principales entre las dos opciones y las buenas prácticas para decidir con conocimiento de causa.
Qué son las identity y los tipos serial
Tanto las “identity” como los tipos “serial” sirven para generar automáticamente valores únicos de primary key. Sin embargo, su implementación y sus funcionalidades son distintas.
🔔 Otra forma de crear valores autoincrementales son las secuencias a secas. En este artículo no hablaremos de ellas porque en muchos casos quieres usar las secuencias “detrás” de serial o identity sin preocuparte de manejarlas directamente. Aun así, en algunos casos las secuencias permiten implementar numeraciones “poco habituales” (por ejemplo, un identificador único para todos los registros de la base de datos o para un conjunto de tablas).
Tipos serial
“Serial” es una funcionalidad específica de PostgreSQL que crea una columna entera vinculada a una secuencia. Es un atajo para crear una secuencia y una columna entera. Los tipos de datos serial no son tipos de verdad, sino una simple comodidad de notación para crear columnas de identificador único (parecida a la propiedad AUTO_INCREMENT que soportan MySQL, MariaDB, MSSQLServer y otras bases de datos).
Los valores enteros generados están limitados a los siguientes tipos de datos enteros.
Los tipos serial son: smallserial, serial y bigserial.
| Nombre | Tamaño en disco | Descripción | Rango |
|---|---|---|---|
| smallserial | 2 bytes | entero autoincremental pequeño | 1 a 32767 |
| serial | 4 bytes | entero autoincremental | 1 a 2147483647 |
| bigserial | 8 bytes | entero autoincremental grande | 1 a 9223372036854775807 |
Declarar una tabla con una primary key serial es algo así:
CREATE TABLE people (
people_id bigserial primary key,
first_name varchar,
last_name varchar
)
que equivale a ejecutar las siguientes sentencias:
CREATE SEQUENCE people_people_id_seq AS bigint;
CREATE TABLE people (
people_id bigint NOT NULL DEFAULT nextval('people_people_id_seq') primary key ,
first_name varchar,
last_name varchar
);
ALTER SEQUENCE people_people_id_seq OWNED BY people.people_id;
Como ves, crear un campo bigserial es un atajo para:
- Crear una columna
biginty hacer que sus valores por defecto se asignen desde un generador de secuencias. - Aplicar una restricción
NOT NULLpara que no se pueda insertar un valor nulo. - Marcar la secuencia como
owned byla columna, de modo que se elimine si se elimina la columna o la tabla.
Hay más información sobre los tipos serial en la documentación de PostgreSQL.
Identity
- Introducidas en PostgreSQL 10, las “identity” cumplen el estándar SQL.
- Separan el concepto de generación de la identidad del tipo de dato, lo que da más libertad al elegir el tipo de dato de la primary key.
- Permiten un control detallado de las propiedades, por ejemplo si se admiten actualizaciones, y se adaptan a distintas restricciones.
- Las identity son más fáciles de gestionar.
Una tabla que usa una IDENTITY como primary key autogenerada se declara así:
CREATE TABLE people (
people_id bigint PRIMARY KEY GENERATED ALWAYS AS IDENTITY,
first_name varchar,
last_name varchar
)
o así
CREATE TABLE people (
people_id bigint PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY,
first_name varchar,
last_name varchar
)
Sobre la cláusula IDENTITY, la documentación de PostgreSQL dice:
La cláusula
IDENTITYcrea la columna como columna identity. Tendrá asociada una secuencia implícita y, en las filas nuevas, la columna recibirá automáticamente valores de esa secuencia. Una columna así es implícitamente NOT NULL.
Vale, pero ¿qué diferencia hay entre las cláusulas ALWAYS y BY DEFAULT?
Las cláusulas
ALWAYSyBY DEFAULTdeterminan cómo se tratan los valores indicados explícitamente por el usuario en los comandosINSERTyUPDATE.En un comando
INSERT, si se eligeALWAYS, un valor indicado por el usuario solo se acepta si la sentenciaINSERTespecificaOVERRIDING SYSTEM VALUE. Si se eligeBY DEFAULT, el valor indicado por el usuario tiene prioridad.En un comando
UPDATE, si se eligeALWAYS, se rechazará cualquier actualización de la columna a un valor distinto deDEFAULT. Si se eligeBY DEFAULT, la columna se puede actualizar con normalidad. (No existe una cláusulaOVERRIDINGpara el comandoUPDATE.)
Hay más información sobre las identity en la documentación de PostgreSQL
Buenas prácticas
Entonces, ¿cuál es mejor y por qué?
✅ Claridad y cumplimiento de los estándares: Usa las “identity” para que el esquema sea más claro. Declaran explícitamente el propósito de la columna y siguen el estándar SQL, lo que mejora la legibilidad y el mantenimiento.
✅ Portabilidad e interoperabilidad: Las “identity” son más compatibles con las prácticas del SQL estándar y facilitan la integración cuando migras bases de datos o trabajas con desarrolladores acostumbrados a las convenciones estándar.
✅ Integridad de los datos y mantenimiento: Usa las “identity” cuando lo importante sea mantener la integridad de los datos y hacer cumplir las restricciones. Poder controlar sus propiedades te da un enfoque completo para gestionar actualizaciones y restricciones. Además, como serial no es un tipo real, no se puede usar en todas las sentencias en las que se puede usar un tipo de campo real. Puedes indicar serial como tipo de columna al crear una tabla o al añadir una columna, pero quitarle la condición de serial a una columna existente, o dársela, no es tan sencillo.
✅ Adaptabilidad a largo plazo: Si prevés cambios futuros en los requisitos de los datos, elige las “identity” por su flexibilidad. Así el esquema queda preparado para posibles cambios.
🔔 ¡ATENCIÓN! ¡REGLA TONTA A LA VISTA! 🔔
¡Usa IDENTITY!
En las aplicaciones nuevas hay que usar columnas identity: úsalas siempre una columna identity, salvo que tengas que dar soporte a versiones antiguas de PostgreSQL (recuerda que las columnas identity llegaron con PostgreSQL v10). Tu código será más manejable, más limpio y más portable.
Conclusión
En el diseño de bases de datos PostgreSQL, elegir entre “identity” y tipos “serial” para las primary key autogeneradas depende de la complejidad del esquema, de las necesidades de rendimiento y de la adaptabilidad a largo plazo. Las “identity” funcionan mejor cuando hacen falta flexibilidad, claridad y respeto a los estándares, mientras que los tipos “serial” sirven para situaciones sencillas y orientadas al rendimiento. Si ajustas la elección a los detalles de tu proyecto, obtendrás un esquema de base de datos sólido y optimizado para tus necesidades. Al final, el objetivo es encontrar el equilibrio entre eficiencia, claridad y preparación para el futuro cuando trabajas con primary key autogeneradas en PostgreSQL.
Artículos relacionados sobre PostgreSQL:
- Tipos compuestos en PostgreSQL: una guía completa - Técnicas avanzadas de modelado de datos
- Un sistema de job queue sencillo pero robusto con Postgres - Patrones prácticos de PostgreSQL
- Cómo acelerar las consultas con LIKE usando pg_trgm - Consejos de optimización del rendimiento
Cursos
Si quieres dominar PostgreSQL, mi empresa ofrece algunos cursos muy solicitados:
- PostgreSQL for Developers, en inglés
- PostgreSQL per Sviluppatori, en italiano
- PostgreSQL per Amministratori, en italiano
Si tu empresa necesita un curso a medida, escríbenos.
Los cursos están disponibles en remoto y en presencial.
Otros enlaces interesantes:
Comments