( tl;dr
:vai all'opzione 3:INSERT con RETURNING )
Ricordiamo che in postgresql non esiste un concetto di "id" per le tabelle, solo sequenze (che sono tipicamente, ma non necessariamente, usati come valori predefiniti per chiavi primarie surrogate, con lo pseudo-tipo SERIAL).
Se sei interessato a ottenere l'ID di una riga appena inserita, ci sono diversi modi:
Opzione 1:CURRVAL(<sequence name>);
.
Ad esempio:
INSERT INTO persons (lastname,firstname) VALUES ('Smith', 'John');
SELECT currval('persons_id_seq');
Il nome della sequenza deve essere noto, è davvero arbitrario; in questo esempio assumiamo che la tabella persons
ha un id
colonna creata con il SERIAL
pseudo-tipo. Per evitare di fare affidamento su questo e sentirti più pulito, puoi usare invece pg_get_serial_sequence
:
INSERT INTO persons (lastname,firstname) VALUES ('Smith', 'John');
SELECT currval(pg_get_serial_sequence('persons','id'));
Avvertenza:currval()
funziona solo dopo un INSERT
(che ha eseguito nextval()
), nella stessa sessione .
Opzione 2:LASTVAL();
È simile alla precedente, solo che non è necessario specificare il nome della sequenza:cerca la sequenza modificata più recente (sempre all'interno della sessione, stesso avvertimento di cui sopra).
Entrambi CURRVAL
e LASTVAL
sono totalmente simultanei sicuri. Il comportamento della sequenza in PG è progettato in modo che sessioni diverse non interferiscano, quindi non c'è rischio di condizioni di gara (se un'altra sessione inserisce un'altra riga tra il mio INSERT e il mio SELECT, ottengo comunque il mio valore corretto).
Tuttavia hanno un sottile problema potenziale. Se il database ha qualche TRIGGER (o REGOLA) che, all'inserimento in persons
table, effettua degli inserimenti extra in altre tabelle... quindi LASTVAL
probabilmente ci darà il valore sbagliato. Il problema può verificarsi anche con CURRVAL
, se gli inserimenti extra vengono eseguiti nelle stesse persons
table (questo è molto meno usuale, ma il rischio esiste ancora).
Opzione 3:INSERT
con RETURNING
INSERT INTO persons (lastname,firstname) VALUES ('Smith', 'John') RETURNING id;
Questo è il modo più pulito, efficiente e sicuro per ottenere l'ID. Non ha nessuno dei rischi del precedente.
Svantaggi? Quasi nessuno:potrebbe essere necessario modificare il modo in cui chiami la tua istruzione INSERT (nel peggiore dei casi, forse il tuo livello API o DB non si aspetta che un INSERT restituisca un valore); non è SQL standard (chi se ne frega); è disponibile da Postgresql 8.2 (dicembre 2006...)
Conclusione:se puoi, scegli l'opzione 3. Altrove, preferisci 1.
Nota:tutti questi metodi sono inutili se intendi ottenere l'ultimo ID inserito a livello globale (non necessariamente dalla tua sessione). Per questo, devi ricorrere a SELECT max(id) FROM table
(ovviamente, questo non leggerà inserti non vincolati da altre transazioni).
Al contrario, dovresti mai usa SELECT max(id) FROM table
invece una delle 3 opzioni sopra, per ottenere l'id appena generato dal tuo INSERT
dichiarazione, perché (a parte le prestazioni) questo non è sicuro concorrente:tra il tuo INSERT
e il tuo SELECT
un'altra sessione potrebbe aver inserito un altro record.