it-swarm.it

Come uso currval () in PostgreSQL per ottenere l'ultimo ID inserito?

Ho un tavolo:

CREATE TABLE names (id serial, name varchar(20))

Voglio l '"ultimo ID inserito" da quella tabella, senza usare RETURNING id Su insert. Sembra che ci sia una funzione CURRVAL() , ma non capisco come usarla.

Ho provato con:

SELECT CURRVAL() AS id FROM names_id_seq
SELECT CURRVAL('names_id_seq')
SELECT CURRVAL('names_id_seq'::regclass)

ma nessuno di loro funziona. Come posso usare currval() per ottenere l'ultimo ID inserito?

70
Jonas

Se crei una colonna come serial PostgreSQL crea automaticamente una sequenza per quello.

Il nome della sequenza è generato automaticamente ed è sempre tablename_columnname_seq, nel tuo caso la sequenza sarà nominata names_id_seq.

Dopo aver inserito nella tabella, puoi chiamare currval() con quel nome di sequenza:

postgres=> CREATE TABLE names in schema_name (id serial, name varchar(20));
CREATE TABLE
postgres=> insert into names (name) values ('Arthur Dent');
INSERT 0 1
postgres=> select currval('names_id_seq');
 currval
---------
       1
(1 row)
postgres=>

Invece di codificare il nome della sequenza, puoi anche usare pg_get_serial_sequence() invece:

select currval(pg_get_serial_sequence('names', 'id'));

In questo modo non è necessario fare affidamento sulla strategia di denominazione utilizzata da Postgres.

Oppure, se non vuoi usare il nome della sequenza, usa lastval()

56

Questo è direttamente da Stack Overflow

Come è stato sottolineato da @a_horse_with_no_name e @Jack Douglas, currval funziona solo con la sessione corrente. Quindi, se sei d'accordo con il fatto che il risultato potrebbe essere influenzato da una transazione non impegnata di un'altra sessione e desideri comunque qualcosa che funzioni tra le sessioni, puoi usare questo:

SELECT last_value FROM your_sequence_name;

Utilizzare il collegamento a SO per ulteriori informazioni.

Da documentazione di Postgres , tuttavia, si afferma chiaramente che

È un errore chiamare lastval se nextval non è stato ancora chiamato nella sessione corrente.

Quindi suppongo che, per usare correttamente currval o last_value per una sequenza tra sessioni, dovresti fare qualcosa del genere?

SELECT setval('serial_id_seq',nextval('serial_id_seq')-1);

Supponendo, ovviamente, che non si avrà un inserto o qualsiasi altro modo di utilizzare il campo seriale nella sessione corrente.

53
Slak

devi chiamare nextval per questa sequenza in questa sessione prima di currval :

create sequence serial;
select nextval('serial');
 nextval
---------
       1
(1 row)

select currval('serial');
 currval
---------
       1
(1 row)

quindi non è possibile trovare l '"ultimo ID inserito" dalla sequenza a meno che insert non venga eseguito nella stessa sessione (una transazione potrebbe eseguire il rollback ma la sequenza no)

come sottolineato nella risposta di a_horse, create table con una colonna di tipo serial creerà automaticamente una sequenza e la utilizzerà per generare il valore predefinito per la colonna, quindi un insert normalmente accede nextval implicitamente:

create table my_table(id serial);
NOTICE:  CREATE TABLE will create implicit sequence "my_table_id_seq" for 
         serial column "my_table.id"

\d my_table
                          Table "stack.my_table"
 Column |  Type   |                       Modifiers
--------+---------+-------------------------------------------------------
 id     | integer | not null default nextval('my_table_id_seq'::regclass)

insert into my_table default values;
select currval('my_table_id_seq');
 currval
---------
       1
(1 row)

Quindi ci sono alcuni problemi con questi vari metodi:

Currval ottiene solo l'ultimo valore generato nella sessione corrente, il che è fantastico se non hai altro che genera valori, ma nei casi in cui potresti chiamare un trigger e/o avere la sequenza avanzata più di una volta nella transazione corrente è non restituirà il valore corretto. Questo non è un problema per il 99% delle persone là fuori - ma è qualcosa che si dovrebbe prendere in considerazione.

Il modo migliore per ottenere l'identificatore univoco assegnato dopo un'operazione di inserimento è utilizzare la clausola RETURNING. L'esempio seguente presuppone che la colonna legata alla sequenza sia denominata "id":

insert into table A (cola,colb,colc) values ('val1','val2','val3') returning id;

Si noti che l'utilità della clausola RETURNING va ben oltre il semplice ottenere la sequenza, poiché inoltre:

  • Restituisce i valori utilizzati per "l'inserimento finale" (dopo, ad esempio, un trigger PRIMA potrebbe aver modificato i dati da inserire.)
  • Restituisce i valori che venivano eliminati:

    elimina dalla tabella A dove id> 100 restituisce *

  • Restituisce le righe modificate dopo un AGGIORNAMENTO:

    aggiorna la tabella A imposta X = 'y' dove blah = 'blech' restituisce *

  • Usa il risultato di un'eliminazione per un aggiornamento:

    CON A as (elimina * dalla tabella A come ID di ritorno) aggiorna B set delete = true dove id in (seleziona id da A);

3
chander

Devi GRANT utilizzo sullo schema, in questo modo:

GRANT USAGE ON SCHEMA schema_name to user;

e

GRANT ALL PRIVILEGES ON schema_name.sequence_name TO user;
1
AMML

Se vuoi ottenere il valore delle sequenze senza aver chiamato nextval() e senza dover modificare la sequenza, ho combinato una funzione PL/pgSQL per gestirlo: Trova il valore di tutti sequenze di database

1
Christophe

Ho dovuto eseguire una query nonostante l'utilizzo di SQLALchemy perché non sono riuscito a utilizzare currval.

nextId = db.session.execute("select last_value from <table>_seq").fetchone()[0] + 1

Questo era un python flask + progetto postgresql.

1
Jalal

In PostgreSQL 11.2 puoi trattare la sequenza come una tabella sembra:

Esempio se hai una sequenza chiamata: 'names_id_seq'

select * from names_id_seq;
 last_value | log_cnt | is_called
------------+---------+-----------
          4 |      32 | t
(1 row)

Questo dovrebbe darti l'ultimo ID inserito (4 in questo caso), il che significa che il valore corrente (o il valore che dovrebbe essere usato per l'id successivo) dovrebbe essere 5.

0
scifisamurai