PostgreSQL
 sql >> Base de données >  >> RDS >> PostgreSQL

Un aperçu du pseudo-type de données série pour PostgreSQL

Présentation

PostgreSQL fournit nativement une riche diversité de types de données prenant en charge de nombreux cas d'utilisation pratiques. Cet article présente l'implémentation spéciale des types de données série généralement utilisés pour la création de clés primaires synthétiques.

Clés uniques

Un précepte fondamental de la théorie de la conception de bases de données est que chaque tuple (c'est-à-dire une ligne) d'une relation (c'est-à-dire une table) doit être identifié de manière unique à partir d'autres tuples. Les attributs, ou colonnes, qui ensemble identifient distinctement un tuple de tous les autres sont appelés une "clé". Certains puristes soutiennent que tout objet ou concept modélisé possède intrinsèquement un attribut ou un ensemble d'attributs pouvant servir de clé et qu'il est important d'identifier cet ensemble d'attributs clés et de les utiliser pour la sélection unique de tuples.

Mais en pratique, l'identification d'un ensemble suffisamment large d'attributs garantissant l'unicité d'un objet modélisé peut s'avérer peu pratique, et donc pour les implémentations réelles, les développeurs se tournent souvent vers des clés synthétiques comme substitut. Autrement dit, plutôt que de s'appuyer sur une combinaison d'attributs réels, une valeur interne à la base de données, généralement des valeurs entières incrémentées, et n'ayant autrement aucune signification physique, est définie comme une clé. En plus de la simplicité d'une clé de colonne unique, le fait qu'il n'y a pas de dépendance dans le monde réel signifie que des facteurs externes ne peuvent jamais obliger à modifier la valeur, comme par exemple, cela pourrait être le cas si le nom d'une personne était utilisé comme clé... puis la personne s'est mariée ou est entrée dans un programme de protection des témoins du gouvernement fédéral et a changé de nom. Même certaines valeurs communément considérées par les profanes comme uniques et immuables, comme le numéro de sécurité sociale américain, ne le sont pas :une personne peut obtenir un nouveau SSN, et les SSN sont parfois réutilisés.

Déclarer un type de données série

PostgreSQL fournit une déclaration de type de données spéciale pour satisfaire ce besoin de clés synthétiques. La déclaration d'une colonne de table de base de données en tant que type SERIAL répond à l'exigence de clés synthétiques en fournissant des entiers uniques lors de l'insertion de nouveaux tuples. Ce pseudo-type de données implémente une colonne de type de données entier avec une valeur par défaut associée dérivée via un appel de fonction qui fournit des valeurs entières incrémentées. Exécution du code suivant pour créer une table simple avec une colonne id de type 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);

Autrement dit, le mot-clé "série" en tant que spécification de type de données implique l'exécution d'instructions DDL créant une colonne de type entier avec une contrainte NOT NULL, une SEQUENCE, puis la valeur par défaut de la colonne est ALTERED pour appeler une fonction intégrée accédant à cette SEQUENCE.

La fonction intégrée nextval effectue un service d'auto-incrémentation :chaque fois que nextval est appelée, elle incrémente le compteur de séquence spécifié et renvoie la valeur nouvellement incrémentée.

Vous pouvez voir le résultat de cet effet en examinant la définition de la table :

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

Insérer des valeurs de série

Pour utiliser la fonctionnalité d'auto-incrémentation, nous insérons simplement des lignes, en nous appuyant sur la valeur par défaut de la colonne de série :

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

Nous voyons qu'une valeur pour la colonne id correspondant à la nouvelle ligne "Alice" a été automatiquement générée. Alternativement, on peut utiliser le mot-clé DEFAULT si l'on souhaite lister explicitement tous les noms de colonne :

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

où nous voyons plus clairement la fonctionnalité d'auto-incrémentation, en attribuant la valeur suivante en série à la nouvelle ligne pour la deuxième insertion de "Bob".

L'insertion de plusieurs lignes fonctionne même :

INSERT INTO person (full_name) VALUES ('Cathy'), ('David');
SELECT * FROM person;
 id | full_name
----+-----------
  1 | Alice
  2 | Bob
  3 | Cathy
  4 | David
(4 rows)
Téléchargez le livre blanc aujourd'hui PostgreSQL Management &Automation with ClusterControlDécouvrez ce que vous devez savoir pour déployer, surveiller, gérer et faire évoluer PostgreSQLTélécharger le livre blanc

Valeurs de série manquantes

La fonction nextval intégrée est optimisée pour les applications non bloquantes à forte concurrence et ne respecte donc pas la restauration. Par conséquent, cela signifie qu'il peut y avoir des valeurs manquantes dans la séquence. Ci-dessous, nous annulons une insertion, mais nous voyons ensuite qu'une insertion ultérieure obtient une nouvelle valeur qui ignore la valeur qui aurait été associée à la transaction abandonnée :

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)

L'avantage de ne pas respecter les rollbacks est que les autres sessions tentant des insertions simultanées ne sont pas bloquées par d'autres sessions d'insertion.

Une autre façon de se retrouver avec des valeurs manquantes est de supprimer des lignes :

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

Notez que même après avoir supprimé la ligne la plus récemment insérée correspondant à la plus grande valeur de colonne d'auto-incrémentation, le compteur de séquence ne revient pas, c'est-à-dire qu'après avoir supprimé la ligne correspondant à 'Fred', pour les insertions suivantes, le compteur de séquence conserve toujours la plus grande valeur précédemment connue et les incréments à partir de là :

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)

Les écarts ou les valeurs manquantes, comme indiqué ci-dessus, seraient considérés comme un problème par certains développeurs d'applications, car sur la liste de diffusion PostgreSQL General, il y a une réitération lente mais régulière de la question de savoir comment éviter les écarts de séquence lors de l'utilisation du pseudo-type de données série. Parfois, il n'y a pas d'exigence commerciale sous-jacente réelle, c'est juste une question d'aversion personnelle pour les valeurs manquantes. Mais il existe des circonstances où la prévention des numéros manquants est un réel besoin, et c'est le sujet d'un article ultérieur.

NON VOUS NE POUVEZ PAS - OUI VOUS POUVEZ !

La contrainte NOT NULL imputée par le pseudo-type de données série protège contre l'insertion de NULL pour la colonne id en rejetant de telles tentatives d'insertion :

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).

Ainsi, nous sommes assurés d'avoir une valeur pour cet attribut.

Cependant, un problème rencontré par certaines personnes est que, comme indiqué ci-dessus, rien n'empêche l'insertion explicite de valeurs, en contournant la valeur d'auto-incrémentation par défaut dérivée via l'invocation de la fonction 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)

Mais deux insertions plus tard en utilisant la valeur par défaut produisent une valeur en double pour la colonne id s'il n'y a pas de vérification de contrainte des valeurs de colonne par rapport à la valeur de séquence :

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 nous utilisions en fait la colonne de l'identifiant de série comme clé, nous l'aurions déclarée comme CLÉ PRIMAIRE ou au moins créé un INDEX UNIQUE. Si nous avions fait cela, l'insertion 'Karen' ci-dessus aurait échoué avec une erreur de clé en double. La version la plus récente de PostgreSQL inclut une nouvelle syntaxe de déclaration de contrainte "générée par défaut en tant qu'identité" qui évite cet écueil et d'autres problèmes hérités liés au pseudo-type de données série.

Fonctions de manipulation de séquence

En plus de la fonction nextval que nous avons déjà mentionnée qui fait avancer la séquence et renvoie la nouvelle valeur, il existe quelques autres fonctions pour interroger et définir les valeurs de la séquence :la fonction currval renvoie la valeur la plus récemment obtenue avec nextval pour la séquence spécifiée, la fonction lastval renvoie la valeur la plus récemment obtenue avec nextval pour toute séquence, et la fonction setval définit la valeur actuelle d'une séquence. Ces fonctions sont appelées avec des requêtes simples., par exemple

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

Et notez que si un appel est fait à la fonction nextval indépendamment de l'exécution réelle d'une insertion, il incrémente la séquence, et cela se reflétera dans les insertions suivantes :

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)

Conclusion

Nous avons introduit une compréhension de base du pseudo-type de données PostgreSQL SERIAL pour les clés synthétiques auto-incrémentées. À titre d'illustration dans cet article, nous avons utilisé la déclaration de type SERIAL, qui crée une colonne d'entiers de 4 octets. PostgreSQL répond à différents besoins de plage avec les pseudo-types de données SMALLSERIAL et BIGSERIAL pour, respectivement, des tailles de colonne de 2 octets et 8 octets. Recherchez un futur article sur un moyen de répondre au besoin de séquences sans valeurs manquantes.