sql >> Base de Datos >  >> RDS >> PostgreSQL

Una descripción general del pseudotipo de datos serial para PostgreSQL

Introducción

PostgreSQL proporciona de forma nativa una rica diversidad de tipos de datos que admiten muchos casos de uso práctico. Este artículo presenta la implementación especial de los tipos de datos en serie que se utilizan normalmente para la creación de claves primarias sintéticas.

Claves únicas

Un precepto fundamental de la teoría del diseño de bases de datos es que cada tupla (es decir, fila) de una relación (es decir, tabla) debe identificarse de forma única de otras tuplas. Los atributos, o columnas, que juntos identifican claramente una tupla de todas las demás se denominan "clave". Algunos puristas sostienen que cualquier objeto o concepto modelado posee inherentemente un atributo o conjunto de atributos que pueden servir como clave y que es importante identificar este conjunto de atributos clave y utilizarlos para la selección única de tuplas.

Pero como cuestión práctica, identificar un conjunto suficientemente grande de atributos que aseguren la singularidad de un objeto modelado puede ser poco práctico y, por lo tanto, para las implementaciones del mundo real, los desarrolladores a menudo recurren a claves sintéticas como sustituto. Es decir, en lugar de depender de alguna combinación de atributos reales, un valor interno de la base de datos, típicamente valores enteros incrementados, y que de otro modo no tiene significado físico, se define como una clave. Además de la simplicidad de una clave de una sola columna, el hecho de que no haya dependencia en el mundo real significa que los factores externos nunca pueden forzar la necesidad de cambiar el valor, como por ejemplo, podría ser el caso si se usa el nombre de una persona. como clave... y luego la persona se casó o ingresó a un programa de protección de testigos del gobierno federal y cambió su nombre. Incluso algunos valores comúnmente considerados por los legos como únicos e inmutables, como el número de seguro social de EE. UU., no lo son:una persona puede obtener un nuevo SSN, y los SSN a veces se reutilizan.

Declaración de un tipo de datos en serie

PostgreSQL proporciona una declaración de tipo de datos especial para satisfacer esta necesidad de claves sintéticas. Declarar una columna de la tabla de la base de datos como tipo SERIAL satisface el requisito de claves sintéticas al proporcionar enteros únicos al insertar nuevas tuplas. Este pseudotipo de datos implementa una columna de tipo de datos enteros con un valor predeterminado asociado derivado a través de una llamada de función que proporciona valores enteros incrementados. Ejecutando el siguiente código para crear una tabla simple con una columna de identificación de tipo serial:

CREATE TABLE person (id serial, full_name text);
actually executes the following DDL
CREATE TABLE person (
    id integer NOT NULL,
    full_name text
);

CREATE SEQUENCE person_id_seq
    START WITH 1
    INCREMENT BY 1
    NO MINVALUE
    NO MAXVALUE
    CACHE 1;
ALTER SEQUENCE person_id_seq OWNED BY person.id;
ALTER TABLE ONLY person
    ALTER COLUMN id
    SET DEFAULT nextval('person_id_seq'::regclass);

Es decir, la palabra clave "serial" como especificación de tipo de datos implica la ejecución de instrucciones DDL que crean una columna de tipo entero con una restricción NOT NULL, una SECUENCIA, y luego el valor predeterminado de la columna se ALTERA para llamar a una función integrada que accede a esa SECUENCIA.

La función incorporada nextval realiza un servicio de autoincremento:cada vez que se llama a nextval, incrementa el contador de secuencia especificado y devuelve el valor recién incrementado.

Puede ver el resultado de este efecto examinando la definición de la tabla:

postgres=# \d person
                   Table "public.person"
  Column   |  Type   |         Modifiers
-----------+---------+-----------------------------------------
 Id        | integer | not null default nextval('person_id_seq'::regclass)
 full_name | text    |

Inserción de valores seriales

Para hacer uso de la funcionalidad de incremento automático, simplemente insertamos filas, confiando en el valor predeterminado para la columna de serie:

INSERT INTO person (full_name) VALUES ('Alice');
SELECT * FROM person;
 id | full_name
----+-----------
  1 | Alice
(1 row)

Vemos que se ha generado automáticamente un valor para la columna id correspondiente a la nueva fila "Alice". Alternativamente, se puede utilizar la palabra clave DEFAULT si se desea enumerar explícitamente todos los nombres de columna:

INSERT INTO person (id, full_name) VALUES (DEFAULT, 'Bob');
SELECT * FROM person;
 id | full_name
----+-----------
  1 | Alice
  2 | Bob
(2 rows)

donde vemos la funcionalidad de incremento automático más aparentemente, asignando el siguiente valor en serie a la nueva fila para la segunda inserción de "Bob".

Insertar varias filas incluso funciona:

INSERT INTO person (full_name) VALUES ('Cathy'), ('David');
SELECT * FROM person;
 id | full_name
----+-----------
  1 | Alice
  2 | Bob
  3 | Cathy
  4 | David
(4 rows)
Descargue el documento técnico hoy Administración y automatización de PostgreSQL con ClusterControlObtenga información sobre lo que necesita saber para implementar, monitorear, administrar y escalar PostgreSQLDescargar el documento técnico

Valores de serie faltantes

La función nextval incorporada está optimizada para aplicaciones de alta concurrencia sin bloqueo y, por lo tanto, no respeta la reversión. En consecuencia, esto significa que pueden faltar valores en la secuencia. A continuación, revertimos una inserción, pero luego vemos que una inserción posterior obtiene un nuevo valor que omite el valor que se habría asociado con la transacción abortada:

BEGIN TRANSACTION;
INSERT INTO person (full_name) VALUES ('Eve');
SELECT * FROM person;
 id | full_name
----+-----------
  1 | Alice
  2 | Bob
  3 | Cathy
  4 | David
  5 | Eve
(5 rows)
ROLLBACK;
INSERT INTO person (full_name) VALUES ('Fred');
SELECT * FROM person;
 id | full_name
----+-----------
  1 | Alice
  2 | Bob
  3 | Cathy
  4 | David
  6 | Fred
(5 rows)

La ventaja de no respetar las reversiones es que otras sesiones que intentan inserciones simultáneas no son bloqueadas por otras sesiones de inserción.

Otra forma de terminar con valores faltantes es si se eliminan las filas:

DELETE FROM person WHERE full_name = 'Cathy';
SELECT * FROM person;
 id | full_name
----+-----------
  1 | Alice
  2 | Bob
  4 | David
  6 | Fred
(4 rows)

Tenga en cuenta que incluso después de eliminar la fila insertada más recientemente correspondiente al valor de columna de identificación de incremento automático más grande, el contador de secuencia no se revierte, es decir, aunque después de eliminar la fila correspondiente a 'Fred', para las inserciones posteriores, el contador de secuencia aún conserva el valor más grande previamente conocido y los incrementos a partir de ahí:

DELETE FROM person WHERE full_name = 'Fred';
INSERT INTO person (full_name) VALUES ('Gina');
SELECT * FROM person;
 id | full_name
----+-----------
  1 | Alice
  2 | Bob
  4 | David
  7 | Gina
(4 rows)

Algunos desarrolladores de aplicaciones consideran que las brechas o los valores faltantes, como se muestra arriba, son un problema porque en la lista de correo general de PostgreSQL, hay una reiteración lenta pero constante de la cuestión de cómo evitar las brechas de secuencia cuando se emplea el pseudotipo de datos en serie. A veces, no existe un requisito comercial subyacente real, es solo una cuestión de aversión personal a los valores faltantes. Pero hay circunstancias en las que evitar que falten números es una necesidad real, y ese es el tema de un artículo posterior.

NO PUEDES - ¡SÍ PUEDES!

La restricción NOT NULL imputada por el pseudotipo de datos serial protege contra la inserción de NULL para la columna id al rechazar dichos intentos de inserción:

INSERT INTO person (id, full_name) VALUES (NULL, 'Henry');
ERROR:  null value in column "id" violates not-null constraint
DETAIL:  Failing row contains (null, Henry).

Por lo tanto, estamos seguros de tener un valor para ese atributo.

Sin embargo, un problema que encuentran algunas personas es que, como se declaró anteriormente, nada impide la inserción explícita de valores, omitiendo el valor de incremento automático predeterminado derivado de la invocación de la función nextval:

INSERT INTO person (id, full_name) VALUES (9, 'Ingrid');
SELECT * FROM person;
 id | full_name
----+-----------
  1 | Alice
  2 | Bob
  4 | David
  7 | Gina
  9 | Ingrid
(5 rows)

Pero luego, dos inserciones más tarde, usando el valor predeterminado, produce un valor duplicado para la columna de identificación si no hay una verificación de restricción de los valores de la columna contra el valor de la secuencia:

INSERT INTO person (full_name) VALUES ('James');
INSERT INTO person (full_name) VALUES ('Karen');
SELECT * FROM person;
 id | full_name
----+-----------
  1 | Alice
  2 | Bob
  4 | David
  7 | Gina
  9 | Ingrid
  8 | James
  9 | Karen
(7 rows)

Si de hecho estuviéramos usando la columna de identificación de serie como clave, la habríamos declarado como CLAVE PRINCIPAL o al menos creado un ÍNDICE ÚNICO. Si hubiéramos hecho eso, entonces la inserción 'Karen' anterior habría fallado con un error de clave duplicada. La versión más reciente de PostgreSQL incluye una nueva sintaxis de declaración de restricciones 'generada de forma predeterminada como identidad' que evita este escollo y algunos otros problemas heredados relacionados con el pseudotipo de datos en serie.

Funciones de manipulación de secuencias

Además de la función nextval que ya mencionamos, que avanza la secuencia y devuelve el nuevo valor, hay algunas otras funciones para consultar y establecer los valores de la secuencia:la función currval devuelve el valor obtenido más recientemente con nextval para la secuencia especificada, la función lastval devuelve el valor obtenido más recientemente con nextval para cualquier secuencia, y la función setval establece el valor actual de una secuencia. Estas funciones se llaman con consultas simples, por ejemplo

SELECT currval('person_id_seq');
 currval
---------
       9
(1 row)

Y tenga en cuenta que si se realiza una llamada a la función nextval independientemente de realizar una inserción, sí incrementa la secuencia, y eso se reflejará en las inserciones posteriores:

SELECT nextval('person_id_seq');
 nextval
---------
      10
(1 row)
INSERT INTO person (full_name) VALUES ('Larry');
SELECT * FROM person;
 id | full_name
----+-----------
  1 | Alice
  2 | Bob
  4 | David
  7 | Gina
  9 | Ingrid
  8 | James
  9 | Karen
 11 | Larry
(8 rows)

Conclusión

Hemos introducido una comprensión básica del pseudotipo de datos SERIAL de PostgreSQL para claves sintéticas autoincrementadas. Para ilustrar este artículo, usamos la declaración de tipo SERIAL, que crea una columna de enteros de 4 bytes. PostgreSQL se adapta a diferentes necesidades de rango con los pseudotipos de datos SMALLSERIAL y BIGSERIAL para tamaños de columna de 2 bytes y 8 bytes, respectivamente. Busque un artículo futuro sobre una forma de abordar la necesidad de secuencias sin valores faltantes.