( tl;dr
:goto option 3 :INSERT with RETURNING )
Rappelez-vous 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'identifiant d'une ligne nouvellement insérée, vous pouvez procéder de plusieurs manières :
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 un id
colonne créée avec le SERIAL
pseudo-type. Pour éviter de dépendre de 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'));
Mise en garde :currval()
ne fonctionne qu'après un INSERT
(qui a 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écurisés simultanément. Le comportement de la séquence dans PG est conçu pour que différentes sessions n'interfèrent pas, il n'y a donc 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 subtil. Si la base de données a un TRIGGER (ou RULE) qui, lors de l'insertion dans persons
table, fait quelques insertions supplémentaires dans d'autres tables... puis LASTVAL
nous donnera probablement la mauvaise valeur. Le problème peut même arriver avec CURRVAL
, si les insertions supplémentaires sont faites dans les mêmes persons
table (c'est beaucoup moins courant, mais le risque existe toujours).
Option 3 :INSERT
avec 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 ne présente 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, peut-être que votre API ou votre couche de base de données ne s'attend pas à ce qu'un INSERT renvoie une valeur) ; ce n'est pas du SQL standard (on s'en fout); il est disponible depuis Postgresql 8.2 (décembre 2006...)
Conclusion :Si vous le pouvez, optez pour l'option 3. Ailleurs, préférez la 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 devriez jamais utilisez SELECT max(id) FROM table
au lieu de l'une des 3 options ci-dessus, pour obtenir l'identifiant qui vient d'être généré par votre INSERT
déclaration, parce que (en dehors des performances) ce n'est pas simultané en toute sécurité :entre votre INSERT
et votre SELECT
une autre session a peut-être inséré un autre enregistrement.