web-dev-qa-db-fra.com

Comment utiliser currval () dans PostgreSQL pour obtenir le dernier identifiant inséré?

J'ai une table:

CREATE TABLE names (id serial, name varchar(20))

Je veux le "dernier identifiant inséré" de cette table, sans utiliser RETURNING id Lors de l'insertion. Il semble y avoir une fonction CURRVAL() , mais je ne comprends pas comment l'utiliser.

J'ai essayé avec:

SELECT CURRVAL() AS id FROM names_id_seq
SELECT CURRVAL('names_id_seq')
SELECT CURRVAL('names_id_seq'::regclass)

mais aucun ne fonctionne. Comment puis-je utiliser currval() pour obtenir le dernier identifiant inséré?

70
Jonas

Si vous créez une colonne comme serial PostgreSQL crée automatiquement une séquence pour cela.

Le nom de la séquence est généré automatiquement et est toujours tablename_columnname_seq, dans votre cas, la séquence sera nommée names_id_seq.

Après l'insertion dans le tableau, vous pouvez appeler currval() avec ce nom de séquence:

postgres=> CREATE TABLE names in schema_name (id serial, name varchar(20));
CREATE TABLE
postgres=> insert into names (name) values ('Arthur Dent');
INSERT 0 1
postgres=> select currval('names_id_seq');
 currval
---------
       1
(1 row)
postgres=>

Au lieu de coder en dur le nom de la séquence, vous pouvez également utiliser pg_get_serial_sequence() à la place:

select currval(pg_get_serial_sequence('names', 'id'));

De cette façon, vous n'avez pas besoin de vous fier à la stratégie de nommage utilisée par Postgres.

Ou si vous ne voulez pas du tout utiliser le nom de la séquence, utilisez lastval()

56

C'est directement de débordement de pile

Comme cela a été souligné par @a_horse_with_no_name et @Jack Douglas, currval ne fonctionne qu'avec la session en cours. Donc, si vous êtes d'accord avec le fait que le résultat pourrait être affecté par une transaction non validée d'une autre session, et que vous voulez toujours quelque chose qui fonctionnera entre les sessions, vous pouvez utiliser ceci:

SELECT last_value FROM your_sequence_name;

Utilisez le lien vers SO pour plus d'informations.

De documentation Postgres cependant, il est clairement indiqué que

C'est une erreur d'appeler lastval si nextval n'a pas encore été appelé dans la session en cours.

Donc, je suppose à strictement parler afin d'utiliser correctement currval ou last_value pour une séquence entre les sessions, vous auriez besoin de faire quelque chose comme ça?

SELECT setval('serial_id_seq',nextval('serial_id_seq')-1);

En supposant, bien sûr, que vous ne disposerez pas d'un insert ou de toute autre manière d'utiliser le champ série dans la session en cours.

53
Slak

Vous devez appeler nextval pour cette séquence dans cette session avant currval :

create sequence serial;
select nextval('serial');
 nextval
---------
       1
(1 row)

select currval('serial');
 currval
---------
       1
(1 row)

vous ne pouvez donc pas trouver le "dernier identifiant inséré" dans la séquence à moins que insert ne soit effectué dans la même session (une transaction peut être annulée mais pas la séquence)

comme indiqué dans la réponse de a_horse, create table avec une colonne de type serial créera automatiquement une séquence et l'utilisera pour générer la valeur par défaut de la colonne, donc un insert accède normalement à nextval implicitement:

create table my_table(id serial);
NOTICE:  CREATE TABLE will create implicit sequence "my_table_id_seq" for 
         serial column "my_table.id"

\d my_table
                          Table "stack.my_table"
 Column |  Type   |                       Modifiers
--------+---------+-------------------------------------------------------
 id     | integer | not null default nextval('my_table_id_seq'::regclass)

insert into my_table default values;
select currval('my_table_id_seq');
 currval
---------
       1
(1 row)

Il y a donc quelques problèmes avec ces différentes méthodes:

Currval n'obtient que la dernière valeur générée dans la session en cours - ce qui est génial si vous n'avez rien d'autre qui génère des valeurs, mais dans les cas où vous pourriez appeler un déclencheur et/ou faire avancer la séquence plus d'une fois dans la transaction en cours, c'est ne va pas retourner la valeur correcte. Ce n'est pas un problème pour 99% des gens - mais c'est quelque chose qu'il faut prendre en considération.

La meilleure façon d'obtenir l'identifiant unique attribué après une opération d'insertion est d'utiliser la clause RETURNING. L'exemple ci-dessous suppose que la colonne liée à la séquence est appelée "id":

insert into table A (cola,colb,colc) values ('val1','val2','val3') returning id;

Notez que l'utilité de la clause RETURNING va bien au-delà de la simple obtention de la séquence, car elle permettra également:

  • Renvoie les valeurs qui ont été utilisées pour la "dernière insertion" (après, par exemple, un déclencheur BEFORE peut avoir modifié les données insérées.)
  • Renvoie les valeurs qui ont été supprimées:

    supprimer du tableau A où id> 100 renvoyant *

  • Renvoie les lignes modifiées après une MISE À JOUR:

    mettre à jour la table A définir X = 'y' où blah = 'blech' renvoyant *

  • Utilisez le résultat d'une suppression pour une mise à jour:

    AVEC A comme (supprimer * du tableau A comme identifiant de retour) mise à jour B set supprimé = vrai où id dans (sélectionner l'identifiant dans A);

3
chander

Vous devez utiliser GRANT sur le schéma, comme ceci:

GRANT USAGE ON SCHEMA schema_name to user;

et

GRANT ALL PRIVILEGES ON schema_name.sequence_name TO user;
1
AMML

Si vous voulez obtenir la valeur des séquences sans avoir appelé nextval(), et sans avoir à modifier la séquence, j'ai créé une fonction PL/pgSQL pour la gérer: Trouver la valeur de tous séquences de base de données

1
Christophe

J'ai dû exécuter une requête malgré l'utilisation de SQLALchemy car je n'ai pas réussi à utiliser currval.

nextId = db.session.execute("select last_value from <table>_seq").fetchone()[0] + 1

C'était un python flask + projet postgresql.

1
Jalal

Dans PostgreSQL 11.2, vous pouvez traiter la séquence comme une table, semble-t-il:

Exemple si vous avez une séquence nommée: 'names_id_seq'

select * from names_id_seq;
 last_value | log_cnt | is_called
------------+---------+-----------
          4 |      32 | t
(1 row)

Cela devrait vous donner le dernier identifiant inséré (4 dans ce cas), ce qui signifie que la valeur actuelle (ou la valeur qui devrait être utilisée pour l'identifiant suivant) devrait être 5.

0
scifisamurai