web-dev-qa-db-fra.com

Fonction PostgreSQL pour le dernier ID inséré

Dans PostgreSQL, comment puis-je insérer le dernier identifiant inséré dans une table?

Dans MS SQL, il y a SCOPE_IDENTITY ().

S'il vous plaît ne me conseillez pas d'utiliser quelque chose comme ça:

select max(id) from table
260
Anton

(tl;dr: goto option 3: INSERT avec RETURNING)

Rappelez-vous qu’en postgresql, il n’existe pas de concept "id" pour les tables, mais seulement séquences (qui sont généralement utilisées, mais pas nécessairement, comme valeurs par défaut pour les clés primaires de substitution, avec le type SERIAL pseudo-type). 

Si vous souhaitez obtenir l'id d'une nouvelle ligne insérée, vous avez plusieurs possibilités:


Option 1: CURRVAL(<sequence name>);

Par exemple:

  INSERT INTO persons (lastname,firstname) VALUES ('Smith', 'John');
  SELECT currval('persons_id_seq');

Le nom de la séquence doit être connu, c'est vraiment arbitraire; Dans cet exemple, nous supposons que la table persons a une colonne id créée avec le pseudo-type SERIAL. Pour éviter de compter sur cela et pour vous sentir plus propre, vous pouvez utiliser à la place pg_get_serial_sequence :

  INSERT INTO persons (lastname,firstname) VALUES ('Smith', 'John');
  SELECT currval(pg_get_serial_sequence('persons','id'));

Avertissement: currval() ne fonctionne qu'après une INSERT (qui a exécuté nextval()), dans la même session.


Option 2: LASTVAL();

Ceci est similaire à la précédente, sauf que vous n'avez pas besoin de spécifier le nom de la séquence: il recherche la séquence modifiée la plus récente (toujours dans votre session, même remarque que ci-dessus).


CURRVAL et LASTVAL sont totalement sûrs en même temps. Le comportement de la séquence dans PG est conçu de manière à ce qu'aucune session différente n'interfère, de sorte qu'il n'y ait aucun risque de conditions de concurrence (si une autre session insère une autre ligne entre mon INSERT et mon SELECT, j'obtiens toujours ma valeur correcte).

Cependant ils ont un problème potentiel subtile. Si la base de données contient des TRIGGER (ou RULE) qui, lors de leur insertion dans la table persons, font des insertions supplémentaires dans d'autres tables ... alors LASTVAL nous donnera probablement une valeur erronée. Le problème peut même arriver avec CURRVAL, si les insertions supplémentaires sont effectuées dans le même tableau persons (c'est beaucoup moins habituel, mais le risque existe toujours).


Option 3: INSERT with RETURNING 

INSERT INTO persons (lastname,firstname) VALUES ('Smith', 'John') RETURNING id;

C'est le moyen le plus propre, le plus efficace et le plus sûr d'obtenir l'identifiant. Il n'a aucun des risques de la précédente. 

Désavantages? Presque pas: vous devrez peut-être modifier la façon dont vous appelez votre instruction INSERT (dans le pire des cas, votre couche API ou DB ne s'attend peut-être pas à ce qu'une valeur INSERT renvoie une valeur); ce n'est pas du SQL standard (peu importe); il est disponible depuis Postgresql 8.2 (déc 2006 ...)


Conclusion: Si vous le pouvez, optez pour l’option 3. Ailleurs, préférez 1.

Remarque: toutes ces méthodes sont inutiles si vous avez l'intention d'obtenir le dernier identificateur inséré globalement (pas nécessairement dans votre session). Pour cela, vous devez recourir à select max(id) from table (bien sûr, cela ne lira pas les insertions non validées d'autres transactions).

494
leonbloy

Voir la clause RETURNING de l'instruction INSERT . En gros, l'INSERT double comme une requête et vous redonne la valeur qui a été insérée. 

70
kwatford

vous pouvez utiliser la clause RETURNING dans une instruction INSERT, exactement comme suit

wgzhao=# create table foo(id int,name text);
CREATE TABLE
wgzhao=# insert into foo values(1,'wgzhao') returning id;
 id 
----
  1
(1 row)

INSERT 0 1
wgzhao=# insert into foo values(3,'wgzhao') returning id;
 id 
----
  3
(1 row)

INSERT 0 1

wgzhao=# create table bar(id serial,name text);
CREATE TABLE
wgzhao=# insert into bar(name) values('wgzhao') returning id;
 id 
----
  1
(1 row)

INSERT 0 1
wgzhao=# insert into bar(name) values('wgzhao') returning id;
 id 
----
  2
(1 row)

INSERT 0 
28
wgzhao

La réponse de Leonbloy est assez complète. J'ajouterais seulement le cas particulier dans lequel il faut obtenir la dernière valeur insérée à l'intérieur d'une fonction PL/pgSQL où OPTION 3 ne convient pas exactement.

Par exemple, si nous avons les tables suivantes:

CREATE TABLE person(
   id serial,
   lastname character varying (50),
   firstname character varying (50),
   CONSTRAINT person_pk PRIMARY KEY (id)
);

CREATE TABLE client (
    id integer,
   CONSTRAINT client_pk PRIMARY KEY (id),
   CONSTRAINT fk_client_person FOREIGN KEY (id)
       REFERENCES person (id) MATCH SIMPLE
);

Si nous devons insérer un enregistrement de client, nous devons nous référer à un enregistrement de personne. Mais supposons que nous voulions concevoir une fonction PL/pgSQL qui insère un nouvel enregistrement dans le client mais prend également en charge l’insertion du nouvel enregistrement de personne. Pour cela, nous devons utiliser une légère variation de l'OPTION 3 de leonbloy:

INSERT INTO person(lastname, firstname) 
VALUES (lastn, firstn) 
RETURNING id INTO [new_variable];

Notez qu'il existe deux clauses INTO. Par conséquent, la fonction PL/pgSQL serait définie comme:

CREATE OR REPLACE FUNCTION new_client(lastn character varying, firstn character varying)
  RETURNS integer AS
$BODY$
DECLARE
   v_id integer;
BEGIN
   -- Inserts the new person record and retrieves the last inserted id
   INSERT INTO person(lastname, firstname)
   VALUES (lastn, firstn)
   RETURNING id INTO v_id;

   -- Inserts the new client and references the inserted person
   INSERT INTO client(id) VALUES (v_id);

   -- Return the new id so we can use it in a select clause or return the new id into the user application
    RETURN v_id;
END;
$BODY$
  LANGUAGE plpgsql VOLATILE;

Maintenant, nous pouvons insérer les nouvelles données en utilisant: 

SELECT new_client('Smith', 'John');

ou 

SELECT * FROM new_client('Smith', 'John');

Et nous obtenons l'id nouvellement créé.

new_client
integer
----------
         1
17
Krauss

Voir l'exemple ci-dessous

CREATE TABLE users (
    -- make the "id" column a primary key; this also creates
    -- a UNIQUE constraint and a b+-tree index on the column
    id    SERIAL PRIMARY KEY,
    name  TEXT,
    age   INT4
);

INSERT INTO users (name, age) VALUES ('Mozart', 20);

Ensuite, pour obtenir le dernier identifiant inséré, utilisez ceci pour la table "utilisateur" nom de colonne seq "id"

SELECT currval(pg_get_serial_sequence('users', 'id'));
8
RINSON KE
SELECT CURRVAL(pg_get_serial_sequence('my_tbl_name','id_col_name'))

Vous devez fournir le nom de la table et le nom de la colonne bien sûr.

Ce sera pour la session/connexion en cours http://www.postgresql.org/docs/8.3/static/functions-sequence.html

7
jishi

Pour ceux qui ont besoin d’obtenir l’ensemble des données, vous pouvez ajouter

returning *

à la fin de votre requête pour obtenir le tout objet, y compris l'id.

3
emreoktem

Essaye ça:

select nextval('my_seq_name');  // Returns next value

Si ce retour 1 (ou quelle que soit la valeur start_value de votre séquence), réinitialisez la séquence à sa valeur d'origine en passant le drapeau false:

select setval('my_seq_name', 1, false);

Autrement, 

select setval('my_seq_name', nextValue - 1, true);

Cela restaurera la valeur de séquence à son état d'origine et "setval" reviendra avec la valeur de séquence que vous recherchez.

1
Oozman

Postgres a un mécanisme intégré pour la même chose qui, dans la même requête, retourne l'identifiant ou ce que vous voulez que la requête renvoie . Considérez que vous avez créé une table comportant 2 colonnes, colonne1 et colonne2, et que vous souhaitez que colonne1 soit renvoyé après chaque insertion.

# create table users_table(id serial not null primary key, name character varying);
CREATE TABLE
#insert into users_table(name) VALUES ('Jon Snow') RETURNING id;
 id 
----
  1
(1 row)

# insert into users_table(name) VALUES ('Arya Stark') RETURNING id;
 id 
----
  2
(1 row)
0
Sandip Debnath