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
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
Risposte:
( 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 persons
abbia una id
colonna creata con lo SERIAL
pseudo-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 CURRVAL
e LASTVAL
sono 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 persons
tabella, fa degli inserimenti extra in altre tabelle ... allora LASTVAL
probabilmente ci darà il valore sbagliato. Il problema può anche verificarsi con CURRVAL
, se gli inserimenti extra vengono eseguiti nella stessa persons
tabella (questo è molto meno normale, ma il rischio esiste ancora).
Opzione 3: INSERT
conRETURNING
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 table
una delle 3 opzioni sopra, per ottenere l'id appena generato dalla tua INSERT
dichiarazione, perché (a parte le prestazioni) questo non è sicuro contemporaneamente: tra te INSERT
e SELECT
un'altra sessione potresti aver inserito un altro record.
SELECT max(id)
sfortunatamente non fa neanche il lavoro non appena inizi a cancellare le righe.
1,2,3,4,5
ed elimina le righe 4 e 5, l'ultimo ID inserito è ancora 5, ma max()
restituisce 3.
RETURNING id;
per inserirlo in un'altra tabella sarebbe il benvenuto!
RETURNING id
all'interno di uno script SQL alimentato allo psql
strumento da riga di comando?
Vedere la clausola RETURNING dell'istruzione INSERT . Fondamentalmente, INSERT raddoppia come una query e restituisce il valore che è stato inserito.
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=$lastid
direttamente. 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 returning
ed è restituito dalla funzione nel suo insieme.
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
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
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'));
SELECT CURRVAL(pg_get_serial_sequence('my_tbl_name','id_col_name'))
Ovviamente devi fornire il nome della tabella e il nome della colonna.
Questo sarà per la sessione / connessione corrente http://www.postgresql.org/docs/8.3/static/functions-sequence.html
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.
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)
Ho avuto questo problema con Java e Postgres. L'ho corretto aggiornando una nuova versione di Connector-J.
postgresql-9.2-1002.jdbc4.jar
https://jdbc.postgresql.org/download.html : versione 42.2.12