Dans PostgreSQL, comment obtenir le dernier identifiant inséré dans une table?
Dans MS SQL, il y a SCOPE_IDENTITY ().
Veuillez ne pas me conseiller d'utiliser quelque chose comme ceci:
select max(id) from table
Dans PostgreSQL, comment obtenir le dernier identifiant inséré dans une table?
Dans MS SQL, il y a SCOPE_IDENTITY ().
Veuillez ne pas me conseiller d'utiliser quelque chose comme ceci:
select max(id) from table
Réponses:
( tl;dr
: passez à l'option 3: INSÉRER avec RETOUR)
Rappelons que dans postgresql, il n'y a pas de concept "id" pour les tables, juste des séquences (qui sont généralement mais pas nécessairement utilisées comme valeurs par défaut pour les clés primaires de substitution, avec le pseudo-type SERIAL ).
Si vous souhaitez obtenir l'ID d'une ligne nouvellement insérée, il existe plusieurs façons:
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 id
colonne créée avec le SERIAL
pseudo-type. Pour éviter de vous y fier 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 un INSERT
(qui s'est exécuté nextval()
), dans la même session .
Option 2: LASTVAL();
Ceci est similaire au précédent, 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 à l'intérieur de votre session, même mise en garde que ci-dessus).
Les deux CURRVAL
et LASTVAL
sont totalement sûrs simultanément. Le comportement de la séquence dans PG est conçu pour que différentes sessions n'interfèrent pas, donc il n'y a aucun risque de conditions de course (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 subtil. Si la base de données a un TRIGGER (ou RULE) qui, lors de l'insertion dans la persons
table, fait des insertions supplémentaires dans d'autres tables ... alors LASTVAL
nous donnera probablement la mauvaise valeur. Le problème peut même se produire avec CURRVAL
, si les insertions supplémentaires sont effectuées dans la même persons
table (c'est beaucoup moins habituel, mais le risque existe toujours).
Option 3: INSERT
avecRETURNING
INSERT INTO persons (lastname,firstname) VALUES ('Smith', 'John') RETURNING id;
C'est le moyen le plus propre, efficace et sûr d'obtenir l'identifiant. Il n'a aucun des risques du précédent.
Désavantages? Presque aucun: 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 pas à ce qu'un 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 identifiant inséré globalement (pas nécessairement par 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).
Inversement, vous ne devez jamais utiliser à la SELECT max(id) FROM table
place l'une des 3 options ci-dessus, pour obtenir l'ID juste généré par votre INSERT
instruction, car (en dehors des performances), ce n'est pas sûr en même temps: entre votre INSERT
et votre SELECT
autre session peut avoir inséré un autre enregistrement.
SELECT max(id)
ne fait malheureusement pas le travail non plus dès que vous commencez à supprimer des lignes.
1,2,3,4,5
et supprimez les lignes 4 et 5, le dernier ID inséré est toujours 5, mais max()
renvoie 3.
RETURNING id;
pour insérer ceci dans un autre tableau serait le bienvenu!
RETURNING id
un script SQL alimenté par l' psql
outil de ligne de commande?
Voir la clause RETURNING de l' instruction INSERT . Fondamentalement, le INSERT se double d'une requête et vous renvoie la valeur qui a été insérée.
insert into names (firstname, lastname) values ('john', 'smith') returning id;
simplement id comme si vous exécutiez select id from names where id=$lastid
directement. Si vous souhaitez enregistrer le retour dans une variable aa , alors insert into names (firstname, lastname) values ('john', 'smith') returning id into other_variable;
si l'instruction contenant le retour est la dernière instruction d'une fonction , l'ID en cours returning
de modification est renvoyé par la fonction dans son ensemble.
vous pouvez utiliser la clause RETURNING dans l'instruction INSERT, tout comme la suivante
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
La réponse de Leonbloy est assez complète. J'ajouterais seulement le cas spécial dans lequel on a besoin d'obtenir la dernière valeur insérée à partir d'une fonction PL / pgSQL où OPTION 3 ne correspond pas exactement.
Par exemple, si nous avons les tableaux suivants:
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 client, nous devons nous référer à un enregistrement personne. Mais disons que nous voulons concevoir une fonction PL / pgSQL qui insère un nouvel enregistrement dans le client mais s'occupe également d'insérer le 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
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'));
SELECT CURRVAL(pg_get_serial_sequence('my_tbl_name','id_col_name'))
Vous devez bien sûr fournir le nom de la table et le nom de la colonne.
Ce sera pour la session / connexion actuelle http://www.postgresql.org/docs/8.3/static/functions-sequence.html
Essaye ça:
select nextval('my_seq_name'); // Returns next value
Si cela retourne 1 (ou quelle que soit la valeur de début de votre séquence), réinitialisez la séquence à sa valeur d'origine, en passant le faux drapeau:
select setval('my_seq_name', 1, false);
Autrement,
select setval('my_seq_name', nextValue - 1, true);
Cela restaurera la valeur de séquence à l'état d'origine et "setval" reviendra avec la valeur de séquence que vous recherchez.
Postgres a un mécanisme intégré pour le même, qui dans la même requête renvoie l'ID ou tout ce que vous souhaitez que la requête retourne. Voici un exemple. Considérez que vous avez créé une table qui comporte 2 colonnes colonne1 et colonne2 et que vous souhaitez que la colonne1 soit renvoyée 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)
J'ai eu ce problème avec Java et Postgres. Je l'ai corrigé en mettant à jour une nouvelle version de Connector-J.
postgresql-9.2-1002.jdbc4.jar
https://jdbc.postgresql.org/download.html : version 42.2.12