Table de comptoir
Il semblerait - qu'est-ce qui est plus facile? Nous y avons installé une assiette séparée - une entrée avec un compteur. Nous avons besoin d'un nouvel identifiant - lu à partir de là pour écrire une nouvelle valeur - faites-le
UPDATE
...
Ne faites pas ça ! Parce que demain, vous devrez résoudre des problèmes:
- verrous qui se chevauchent persistants lorsque vous
UPDATE
voyez Antipatterns PostgreSQL: combattre des hordes de "morts" - dégradation progressive de la vitesse d'accès aux données de la table de comptage
voir Antipatterns PostgreSQL: mise à jour d'une grande table sous charge - ... et la nécessité de le nettoyer avec des transactions actives qui vous dérangeront ,
voir DBA: lorsque VACUUM passe, nous nettoyons la table manuellement
Objet SEQUENCE
Pour de telles tâches, PostgreSQL fournit une entité distincte -
SEQUENCE
. Elle n'est pas transactionnelle , c'est-à-dire qu'elle ne provoque pas de verrous , mais deux transactions "parallèles" recevront certainement des valeurs différentes .
Pour obtenir l'ID suivant d'une séquence, utilisez simplement la fonction
nextval
:
SELECT nextval('seq_name'::regclass);
Parfois, vous devez obtenir plusieurs identifiants à la fois - pour l'enregistrement en continu via COPY, par exemple. Utiliser pour cela
setval(currval() + N)
est fondamentalement faux ! Pour la simple raison qu'entre les appels aux fonctions "interne" ( currval
) et "externe" ( setval
), une transaction concurrente peut changer la valeur actuelle de la séquence. La bonne façon est d'appeler le nextval
nombre de fois requis:
SELECT
nextval('seq_name'::regclass)
FROM
generate_series(1, N);
Pseudo série
Il n'est pas très pratique de travailler avec des séquences en mode "manuel". Mais notre tâche typique est d'assurer l'insertion d'un nouvel enregistrement avec un nouvel ID de séquence! Particulièrement dans ce but, PostgreSQL est inventé
serial
, qui, lors de la génération d'une table, "se développe" en quelque chose comme .
Il n'est pas nécessaire de se souvenir du nom de la séquence générée automatiquement liée au champ, il existe une fonction pour cela . La même fonction peut être utilisée dans vos propres substitutions - par exemple, s'il est nécessaire de créer une séquence commune pour plusieurs tables à la fois.
Cependant, étant donné que l'utilisation de la séquence n'est pas transactionnelle, si l'identifiant de celle-ci a été reçu par une transaction annulée, la séquence d'identifiants dans les enregistrements de table sauvegardés sera "fuyante"id integer NOT NULL DEFAULT nextval('tbl_id_seq')
pg_get_serial_sequence(table_name, column_name)
DEFAULT
...
Colonnes générées
À partir de PostgreSQL 10 , il est possible de déclarer une colonne d'identité (
GENERATED AS IDENTITY
) conforme à la norme SQL: 2003. Dans la variante, le GENERATED BY DEFAULT
comportement est équivalent serial
, mais avec GENERATED ALWAYS
tout ce qui est plus intéressant:
CREATE TABLE tbl(
id
integer
GENERATED ALWAYS AS IDENTITY
);
INSERT INTO tbl(id) VALUES(DEFAULT);
-- : 10 .
INSERT INTO tbl(id) VALUES(1);
-- ERROR: cannot insert into column "id"
-- DETAIL: Column "id" is an identity column defined as GENERATED ALWAYS.
-- HINT: Use OVERRIDING SYSTEM VALUE to override.
Oui, afin d'insérer une valeur spécifique "à travers" une telle colonne, vous devez travailler davantage avec
OVERRIDING SYSTEM VALUE
:
INSERT INTO tbl(id) OVERRIDING SYSTEM VALUE VALUES(1);
-- : 11 .
Notez que maintenant nous avons deux valeurs identiques dans la table
id = 1
- c'est-à-dire que GENERATED n'impose pas de conditions et d'indices UNIQUE supplémentaires , mais est purement une déclaration, ainsi que serial
.
En général, sur les versions modernes de PostgreSQL, l' utilisation de serial est déconseillée, avec le remplacement préféré de
GENERATED
. Sauf, peut-être, la situation de prise en charge des applications multi-versions fonctionnant avec PG inférieur à 10.
UUID généré
Tout va bien tant que vous travaillez dans une seule instance de base de données. Mais lorsqu'il y en a plusieurs, il n'y a pas de moyen adéquat de synchroniser les séquences (cependant, cela ne vous empêche pas de les synchroniser «mal» , si vous le souhaitez vraiment). C'est là que le type
UUID
et les fonctions pour générer des valeurs pour lui viennent à la rescousse . Je l'utilise généralement uuid_generate_v4()
comme le plus "occasionnel".
Champs système masqués
tableoid / ctid
Parfois, lors de l'extraction d'enregistrements à partir d'une table, vous devez d'une manière ou d'une autre adresser un enregistrement "physique" spécifique, ou trouver à partir de quelle section particulière un enregistrement particulier a été obtenu lors de l'accès à la table "parent" en utilisant l' héritage .
Dans ce cas, les champs système cachés présents dans chaque enregistrement nous aideront :
tableoid
stocke leoid
-id de la table - c'est-à-diretableoid::regclass::text
donne le nom d'une section de table particulièrectid
- adresse "physique" de l'enregistrement au format(<>,<>)
Par exemple,
ctid
il peut être utilisé pour des opérations avec une table sans clé primaire , mais tableoid
pour l'implémentation de certains types de clés étrangères.
oid
Il était possible de déclarer jusqu'à 11 PostgreSQL lors de la création de la table attributaire
WITH OIDS
:
CREATE TABLE tbl(id serial) WITH OIDS;
Chaque entrée de cette table reçoit un champ caché supplémentaire
oid
avec une valeur globalement unique dans la base de données - comme il était organisé pour les tables système comme pg_class
, pg_namespace
...
Lorsque vous insérez un enregistrement dans une table, la valeur générée est renvoyée immédiatement au résultat de la requête:
INSERT INTO tbl(id) VALUES(DEFAULT);
: OID 16400 11 .
Un tel champ est invisible pour une requête de table "normale":
SELECT * FROM tbl;
id
--
1
Comme les autres champs système, il doit être demandé explicitement:
SELECT tableoid, ctid, xmin, xmax, cmin, cmax, oid, * FROM tbl;
tableoid | ctid | xmin | xmax | cmin | cmax | oid | id
---------------------------------------------------------
16596 | (0,1) | 572 | 0 | 0 | 0 | 16400 | 1
Certes, la valeur
oid
n'est que de 32 bits , il est donc très facile d'obtenir un débordement, après quoi oid
il ne sera même pas possible de créer une table (il en faut une nouvelle !). Par conséquent, depuis PostgreSQL 12, il WITH OIDS
n'est plus pris en charge .
Heure "juste" clock_timestamp
Parfois, lors d'une longue exécution d'une requête ou d'une procédure, vous souhaitez lier l'heure "actuelle" à l'enregistrement. L'échec attend quiconque essaie d'utiliser la fonction pour ce faire
now()
- il renverra la même valeur tout au long de la transaction .
Pour obtenir l'heure «maintenant», il y a une fonction
clock_timestamp()
(et un autre groupe de ses frères). La différence entre le comportement de ces fonctions peut être vue sur l'exemple d'une requête simple:
SELECT
now()
, clock_timestamp()
FROM
generate_series(1, 4);
now | clock_timestamp
-------------------------------+-------------------------------
2020-08-19 16:26:05.626629+03 | 2020-08-19 16:26:05.626758+03
2020-08-19 16:26:05.626629+03 | 2020-08-19 16:26:05.626763+03
2020-08-19 16:26:05.626629+03 | 2020-08-19 16:26:05.626764+03
2020-08-19 16:26:05.626629+03 | 2020-08-19 16:26:05.626765+03