Função PostgreSQL para o último ID inserido

321

No PostgreSQL, como obtenho o último ID inserido em uma tabela?

No MS SQL, há SCOPE_IDENTITY ().

Por favor, não me aconselhe a usar algo como isto:

select max(id) from table
Anton
fonte
por que você odeia a função max? Eu acho muito simples. Existe algum problema como segurança?
jeongmin.cha
9
@ jeongmin.cha há problema se entre existem outras operações, e mais inserções (operações simultâneas), meios max id mudou, a menos e até que você tome um bloqueio de forma explícita e não liberá-lo
rohanagarwal
Se o caso de uso pretendido for usar o último ID inserido como parte do valor de uma inserção subsequente, consulte esta pergunta .
Flux

Respostas:

606

( tl;dr: goto opção 3: INSERT com RETURNING)

Lembre-se de que no postgresql não existe um conceito de "id" para tabelas, apenas seqüências (que geralmente são usadas, mas não necessariamente, como valores padrão para chaves primárias substitutas, com o pseudo-tipo SERIAL ).

Se você estiver interessado em obter o ID de uma linha recém-inserida, há várias maneiras:


Opção 1: CURRVAL(<sequence name>);.

Por exemplo:

  INSERT INTO persons (lastname,firstname) VALUES ('Smith', 'John');
  SELECT currval('persons_id_seq');

O nome da sequência deve ser conhecido, é realmente arbitrário; neste exemplo, assumimos que a tabela personstenha uma idcoluna criada com o SERIALpseudo-tipo. Para evitar confiar nisso e se sentir mais limpo, você pode usar pg_get_serial_sequence:

  INSERT INTO persons (lastname,firstname) VALUES ('Smith', 'John');
  SELECT currval(pg_get_serial_sequence('persons','id'));

Advertência: currval()só funciona após um INSERT(que foi executado nextval()), na mesma sessão .


Opção 2: LASTVAL();

Isso é semelhante ao anterior, mas você não precisa especificar o nome da sequência: ele procura a sequência modificada mais recente (sempre dentro da sua sessão, a mesma ressalva acima).


Ambos CURRVALe LASTVALsão totalmente simultâneos seguros. O comportamento da sequência no PG é projetado para que uma sessão diferente não interfira, portanto não há risco de condições de corrida (se outra sessão inserir outra linha entre o meu INSERT e meu SELECT, ainda assim recebo meu valor correto).

No entanto, eles têm um problema potencial sutil. Se o banco de dados tiver algum TRIGGER (ou RULE) que, ao ser inserido na personstabela, faça algumas inserções extras em outras tabelas ... LASTVALprovavelmente nos fornecerá o valor errado. O problema pode até acontecer CURRVALse as inserções extras forem feitas na mesma personstabela (isso é muito menos usual, mas o risco ainda existe).


Opção 3: INSERTcomRETURNING

INSERT INTO persons (lastname,firstname) VALUES ('Smith', 'John') RETURNING id;

Essa é a maneira mais limpa, eficiente e segura de obter o ID. Não tem nenhum dos riscos do anterior.

Desvantagens? Quase nenhum: você pode precisar modificar a maneira como chama sua instrução INSERT (na pior das hipóteses, talvez sua camada API ou DB não espere que uma INSERT retorne um valor); não é SQL padrão (quem se importa); está disponível desde o Postgresql 8.2 (dez 2006 ...)


Conclusão: Se puder, vá para a opção 3. Em outros lugares, prefira 1.

Nota: todos esses métodos são inúteis se você pretende obter o último ID inserido globalmente (não necessariamente pela sua sessão). Para isso, você deve recorrer a SELECT max(id) FROM table(é claro, isso não lerá inserções não confirmadas de outras transações).

Por outro lado, você nunca deve usar SELECT max(id) FROM tableuma das três opções acima para obter o ID gerado apenas por sua INSERTdeclaração, porque (além do desempenho) isso não é seguro simultaneamente: entre você INSERTe sua SELECToutra sessão pode ter inserido outro registro.

leonbloy
fonte
25
LASTVAL () pode ser muito ruim, caso você adicione uma regra de gatilho / inserção de linhas em si mesma em outra tabela.
Kouber Saparev
3
SELECT max(id)infelizmente não faz o trabalho assim que você começa a excluir linhas.
Simon A. Eugster
2
@leonbloy A menos que eu perdi alguma coisa, se você tem linhas com IDs 1,2,3,4,5e excluir as linhas 4 e 5, o último ID inserido ainda é 5, mas max()retorna 3.
Simon A. Eugster
9
Uma amostra de como usar RETURNING id;para inserir isso em outra tabela seria bem-vinda!
Olivier Pons
8
Como posso usar RETURNING iddentro de um script SQL alimentado para a psqlferramenta de linha de comando?
amoe
82

Consulte a cláusula RETURNING da instrução INSERT . Basicamente, o INSERT funciona como uma consulta e devolve o valor que foi inserido.

Kwatford
fonte
4
Funciona a partir da versão 8.2 e é a melhor e mais rápida solução.
Frank Heikens
9
talvez uma explicação rápida de como usar o referido ID retornado?
Andrew
@ Andrew, não tenho certeza se entendi a pergunta. Você não sabe recuperar resultados de uma consulta? Depende do idioma / biblioteca e deve funcionar da mesma maneira, independentemente de você estar fazendo uma inserção de seleção ou de retorno. A única outra interpretação que posso sugerir é que você recuperou o ID da chamada com sucesso e não sabe para que serve ... nesse caso, por que você o recuperou?
precisa saber é o seguinte
8
@ Andrew, se você executar a partir de linha de comando psql isso: insert into names (firstname, lastname) values ('john', 'smith') returning id;, então ele simplesmente saídas id como se você correu select id from names where id=$lastiddiretamente. Se você deseja salvar o retorno em uma variável , insert into names (firstname, lastname) values ('john', 'smith') returning id into other_variable;se a declaração que contém o retorno é a última em uma função , o ID sendo returningretornado é retornado pela função como um todo.
Alexander Bird
32

você pode usar a cláusula RETURNING na instrução INSERT, da seguinte maneira

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 
wgzhao
fonte
28

A resposta de Leonbloy é bastante completa. Eu acrescentaria apenas o caso especial em que é necessário obter o último valor inserido de uma função PL / pgSQL em que a OPÇÃO 3 não se encaixa exatamente.

Por exemplo, se tivermos as seguintes tabelas:

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 precisarmos inserir um registro de cliente, devemos nos referir a um registro de pessoa. Mas digamos que desejamos criar uma função PL / pgSQL que insira um novo registro no cliente, mas também cuide da inserção do novo registro de pessoa. Para isso, devemos usar uma pequena variação da OPÇÃO 3 de leonbloy:

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

Observe que existem duas cláusulas INTO. Portanto, a função PL / pgSQL seria definida como:

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;

Agora podemos inserir os novos dados usando:

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

ou

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

E obtemos o ID recém-criado.

new_client
integer
----------
         1
Krauss
fonte
9

Para aqueles que precisam obter o registro de todos os dados, você pode adicionar

returning *

até o final da sua consulta para obter o objeto all, incluindo o ID.

emreoktem
fonte
8

Veja o exemplo abaixo

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

Para obter o último ID inserido, use-o na tabela "user" seq nome da coluna "id"

SELECT currval(pg_get_serial_sequence('users', 'id'));
RINSON KE
fonte
1

Tente o seguinte:

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

Se isso retornar 1 (ou qualquer que seja o valor inicial da sua sequência), redefina a sequência novamente para o valor original, passando o sinalizador falso:

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

De outra forma,

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

Isso restaurará o valor da sequência para o estado original e "setval" retornará com o valor da sequência que você está procurando.

Oozman
fonte
1

O Postgres possui um mecanismo embutido para o mesmo, que na mesma consulta retorna o ID ou o que você deseja que a consulta retorne. aqui está um exemplo. Considere que você criou uma tabela que possui 2 colunas column1 e column2 e deseja que a coluna1 seja retornada após cada inserção.

# 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)
Sandip Debnath
fonte