Funzione PostgreSQL per l'ultimo ID inserito


321

In PostgreSQL, come posso inserire l'ultimo ID inserito in una tabella?

In MS SQL c'è SCOPE_IDENTITY ().

Per favore, non mi consiglia di usare qualcosa del genere:

select max(id) from table

perché odi la funzione massima? Penso che sia molto semplice. C'è qualche problema come la sicurezza?
jeongmin.cha,

9
@ jeongmin.cha c'è un problema se nel mezzo ci sono altre operazioni e più inserimenti (operazioni simultanee), significa che l'id massimo è cambiato, a meno che e fino a quando non si prende esplicitamente un blocco e non lo si rilascia
rohanagarwal

Se il caso d'uso previsto è quello di utilizzare l'ultimo ID inserito come parte del valore di un inserimento successivo, vedere questa domanda .
Flux

Risposte:


606

( tl;dr: vai all'opzione 3: INSERISCI con RETURNING)

Ricordiamo che in postgresql non esiste un concetto "id" per le tabelle, ma solo sequenze (che sono in genere ma non necessariamente utilizzate come valori predefiniti per le 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>);.

Per 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 supponiamo che la tabella personsabbia una idcolonna creata con lo SERIALpseudo-tipo. Per evitare di fare affidamento su questo e per sentirti più pulito, puoi invece utilizzare 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 è stato eseguito nextval()), nella stessa sessione .


Opzione 2: LASTVAL();

Questo è simile al precedente, solo che non è necessario specificare il nome della sequenza: cerca la sequenza modificata più recente (sempre all'interno della sessione, lo stesso avvertimento di cui sopra).


Entrambi CURRVALe LASTVALsono totalmente sicuri contemporaneamente. Il comportamento della sequenza in PG è progettato in modo tale che diverse sessioni non interferiscano, quindi non vi è alcun 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 potenziale problema sottile. Se il database ha un TRIGGER (o RULE) che, inserendolo in una personstabella, fa degli inserimenti extra in altre tabelle ... allora LASTVALprobabilmente ci darà il valore sbagliato. Il problema può anche verificarsi con CURRVAL, se gli inserimenti extra vengono eseguiti nella stessa personstabella (questo è molto meno normale, ma il rischio esiste ancora).


Opzione 3: INSERTconRETURNING

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: potresti dover modificare il modo in cui chiami la tua istruzione INSERT (nel peggiore dei casi, forse il tuo livello API o DB non prevede 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 si intende ottenere l'ultimo ID inserito a livello globale (non necessariamente dalla propria sessione). Per questo, è necessario ricorrere a SELECT max(id) FROM table(ovviamente, questo non leggerà inserimenti non impegnati da altre transazioni).

Al contrario, non dovresti mai usare SELECT max(id) FROM tableuna delle 3 opzioni sopra, per ottenere l'id appena generato dalla tua INSERTdichiarazione, perché (a parte le prestazioni) questo non è sicuro contemporaneamente: tra te INSERTe SELECTun'altra sessione potresti aver inserito un altro record.


25
LASTVAL () potrebbe essere molto malvagio, nel caso in cui aggiungi un trigger / regola inserendo righe su se stesso in un'altra tabella.
Kouber Saparev,

3
SELECT max(id)sfortunatamente non fa neanche il lavoro non appena inizi a cancellare le righe.
Simon A. Eugster,

2
@leonbloy A meno che non mi sia perso qualcosa, se hai righe con ID 1,2,3,4,5ed elimina le righe 4 e 5, l'ultimo ID inserito è ancora 5, ma max()restituisce 3.
Simon A. Eugster

9
Un esempio di come utilizzare RETURNING id;per inserirlo in un'altra tabella sarebbe il benvenuto!
Olivier Pons il

8
Come posso usare RETURNING idall'interno di uno script SQL alimentato allo psqlstrumento da riga di comando?
Amoe

82

Vedere la clausola RETURNING dell'istruzione INSERT . Fondamentalmente, INSERT raddoppia come una query e restituisce il valore che è stato inserito.


4
Funziona dalla versione 8.2 ed è la soluzione migliore e più veloce.
Frank Heikens,

9
forse una rapida spiegazione di come utilizzare detto ID restituito?
Andrew,

@Andrew Non sono sicuro di aver capito la domanda. Non sai come recuperare i risultati da una query? Dipende dalla lingua / dalla libreria e dovrebbe funzionare allo stesso modo indipendentemente dal fatto che si stia eseguendo una selezione o un inserto di ritorno. L'unica altra interpretazione che posso trovare è che hai recuperato correttamente l'ID dalla chiamata e non sai a cosa serve ... in tal caso, perché l'hai recuperato?
kwatford,

8
@Andrew, se corri dalla riga di comando psql questo :, insert into names (firstname, lastname) values ('john', 'smith') returning id;allora semplicemente emette id come se tu stessi eseguendo select id from names where id=$lastiddirettamente. Se si desidera salvare il ritorno in una variabile , quindi insert into names (firstname, lastname) values ('john', 'smith') returning id into other_variable;Se l'istruzione che contiene il ritorno è l' ultima istruzione in una funzione , l'ID che viene returninged è restituito dalla funzione nel suo insieme.
Alexander Bird,

32

puoi usare la clausola RETURNING nell'istruzione INSERT, proprio come il seguente

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

La risposta di Leonbloy è abbastanza completa. Vorrei solo aggiungere il caso speciale in cui è necessario ottenere l'ultimo valore inserito da una funzione PL / pgSQL in cui OPZIONE 3 non si adatta esattamente.

Ad esempio, se abbiamo le seguenti tabelle:

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
);

Se dobbiamo inserire un record cliente, dobbiamo fare riferimento a un record persona. Ma supponiamo di voler escogitare una funzione PL / pgSQL che inserisca un nuovo record nel client ma si occupi anche dell'inserimento del nuovo record della persona. Per questo, dobbiamo usare una leggera variazione dell'OPZIONE 3 di leonbloy:

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

Si noti che esistono due clausole INTO. Pertanto, la funzione PL / pgSQL sarebbe definita come:

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;

Ora possiamo inserire i nuovi dati usando:

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

o

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

E otteniamo l'ID appena creato.

new_client
integer
----------
         1

9

Per quelli che hanno bisogno di ottenere tutti i record di dati, è possibile aggiungere

returning *

alla fine della query per ottenere tutto l'oggetto incluso l'id.


8

Vedi l'esempio seguente

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);

Quindi, per ottenere l'ultimo ID inserito, usalo per la tabella "user" seq nome colonna "id"

SELECT currval(pg_get_serial_sequence('users', 'id'));


1

Prova questo:

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

Se questo restituisce 1 (o qualunque sia il valore iniziale per la sequenza), reimposta la sequenza sul valore originale, passando il flag falso:

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

Altrimenti,

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

Ciò ripristinerà il valore della sequenza allo stato originale e "setval" tornerà con il valore della sequenza che stai cercando.


1

Postgres ha un meccanismo integrato per lo stesso, che nella stessa query restituisce l'id o qualunque cosa tu voglia restituire. ecco un esempio. Si consideri che è stata creata una tabella con 2 colonne column1 e column2 e si desidera che la colonna1 venga restituita dopo ogni inserimento.

# 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)

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.