Become a member!

¿SERIAL o IDENTITY en PostgreSQL? El autoincremento que la mayoría de los esquemas elige mal

🌐
Este artículo también está disponible en otros idiomas:
🇩🇪 Deutsch  •  🇧🇷 Português  •  🇬🇧 English

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.

NombreTamaño en discoDescripciónRango
smallserial2 bytesentero autoincremental pequeño1 a 32767
serial4 bytesentero autoincremental1 a 2147483647
bigserial8 bytesentero autoincremental grande1 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 bigint y hacer que sus valores por defecto se asignen desde un generador de secuencias.
  • Aplicar una restricción NOT NULL para que no se pueda insertar un valor nulo.
  • Marcar la secuencia como owned by la 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 IDENTITY crea 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 ALWAYS y BY DEFAULT determinan cómo se tratan los valores indicados explícitamente por el usuario en los comandos INSERT y UPDATE.

En un comando INSERT, si se elige ALWAYS, un valor indicado por el usuario solo se acepta si la sentencia INSERT especifica OVERRIDING SYSTEM VALUE. Si se elige BY DEFAULT, el valor indicado por el usuario tiene prioridad.

En un comando UPDATE, si se elige ALWAYS, se rechazará cualquier actualización de la columna a un valor distinto de DEFAULT. Si se elige BY DEFAULT, la columna se puede actualizar con normalidad. (No existe una cláusula OVERRIDING para el comando UPDATE.)

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:

Cursos

Si quieres dominar PostgreSQL, mi empresa ofrece algunos cursos muy solicitados:

Si tu empresa necesita un curso a medida, escríbenos.

Los cursos están disponibles en remoto y en presencial.

Otros enlaces interesantes:

Comments