Función PostgreSQL para la última ID insertada


321

En PostgreSQL, ¿cómo puedo insertar la última identificación en una tabla?

En MS SQL hay SCOPE_IDENTITY ().

Por favor no me aconsejen usar algo como esto:

select max(id) from table

¿Por qué odias la función max? Pienso que es muy simple. ¿Hay algún problema como la seguridad?
jeongmin.cha

99
@ jeongmin.cha hay un problema si en el medio hay otras operaciones y más inserciones (operaciones concurrentes), significa que la identificación máxima ha cambiado, a menos y hasta que tome un bloqueo explícitamente y no lo libere
rohanagarwal

Si el caso de uso previsto es utilizar la última ID insertada como parte del valor de una inserción posterior, consulte esta pregunta .
Flux

Respuestas:


606

( tl;dr: goto opción 3: INSERTAR con RETORNO)

Recuerde que en postgresql no hay un concepto de "id" para las tablas, solo secuencias (que normalmente se usan, pero no necesariamente, como valores predeterminados para las claves primarias sustitutas, con el pseudotipo SERIAL ).

Si está interesado en obtener la identificación de una fila recién insertada, hay varias formas:


Opción 1: CURRVAL(<sequence name>);.

Por ejemplo:

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

El nombre de la secuencia debe ser conocido, es realmente arbitrario; En este ejemplo, suponemos que la tabla personstiene una idcolumna creada con el SERIALpseudotipo. Para evitar confiar en esto y sentirse más limpio, puede usar en su lugar pg_get_serial_sequence:

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

Advertencia: currval()solo funciona después de un INSERT(que se ha ejecutado nextval()), en la misma sesión .


Opcion 2: LASTVAL();

Esto es similar al anterior, solo que no necesita especificar el nombre de la secuencia: busca la secuencia modificada más reciente (siempre dentro de su sesión, la misma advertencia que la anterior).


Ambos CURRVALy LASTVALson totalmente concurrentes seguros. El comportamiento de la secuencia en PG está diseñado para que una sesión diferente no interfiera, por lo que no hay riesgo de condiciones de carrera (si otra sesión inserta otra fila entre mi INSERT y mi SELECT, aún obtengo mi valor correcto).

Sin embargo, tienen un problema potencial sutil. Si la base de datos tiene algún DISPARADOR (o REGLA) que, al insertarlo en la personstabla, hace algunas inserciones adicionales en otras tablas ... entonces LASTVALprobablemente nos dará el valor incorrecto. El problema puede suceder incluso CURRVALsi las inserciones adicionales se realizan en la misma personstabla (esto es mucho menos habitual, pero el riesgo aún existe).


Opción 3: INSERTconRETURNING

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

Esta es la forma más limpia, eficiente y segura de obtener la identificación. No tiene ninguno de los riesgos de lo anterior.

Inconvenientes? Casi ninguno: es posible que deba modificar la forma en que llama a su instrucción INSERT (en el peor de los casos, quizás su capa API o DB no espera que un INSERT devuelva un valor); no es SQL estándar (a quién le importa); está disponible desde Postgresql 8.2 (diciembre de 2006 ...)


Conclusión: si puede, vaya a la opción 3. En otro lugar, prefiera 1.

Nota: todos estos métodos son inútiles si tiene la intención de obtener la última identificación insertada globalmente (no necesariamente por su sesión). Para esto, debe recurrir a SELECT max(id) FROM table(por supuesto, esto no leerá inserciones no confirmadas de otras transacciones).

Por el contrario, nunca debe utilizar SELECT max(id) FROM tableuna de las 3 opciones anteriores, para obtener la identificación que acaba de generar su INSERTdeclaración, porque (aparte del rendimiento) esto no es concurrentemente seguro: entre usted INSERTy su SELECTotra sesión podría haber insertado otro registro.


25
LASTVAL () puede ser muy malo, en caso de que agregue un desencadenador / regla que inserte filas en sí mismo en otra tabla.
Kouber Saparev

3
SELECT max(id)desafortunadamente, tampoco hace el trabajo tan pronto como comienza a eliminar filas.
Simon A. Eugster

2
@leonbloy A menos que me haya perdido algo, si tiene filas con ID 1,2,3,4,5y elimina las filas 4 y 5, la última ID insertada sigue siendo 5, pero max()devuelve 3.
Simon A. Eugster

99
¡Una muestra de cómo usar RETURNING id;para insertar esto en otra tabla sería bienvenida!
Olivier Pons

8
¿Cómo puedo usar RETURNING iddentro de un script SQL alimentado a la psqlherramienta de línea de comando?
amoe

82

Consulte la cláusula RETURNING de la declaración INSERT . Básicamente, INSERT funciona como una consulta y le devuelve el valor que se insertó.


44
Funciona a partir de la versión 8.2 y es la mejor y más rápida solución.
Frank Heikens

99
tal vez una explicación rápida de cómo usar dicha identificación devuelta?
Andrew

@ Andrew No estoy seguro de entender la pregunta. ¿No sabes cómo recuperar resultados de una consulta? Eso depende del idioma / biblioteca y debería funcionar igual independientemente de si está haciendo una selección o una inserción de retorno. La única otra interpretación que se me ocurre es que ha recuperado con éxito la identificación de la llamada y no sabe para qué sirve ... en cuyo caso, ¿por qué la estaba recuperando?
kwatford

8
@ Andrew, si ejecuta desde la línea de comando psql esto: insert into names (firstname, lastname) values ('john', 'smith') returning id;entonces simplemente genera la identificación como si se ejecutara select id from names where id=$lastiddirectamente. Si desea guardar el retorno en una variable aa , entonces, insert into names (firstname, lastname) values ('john', 'smith') returning id into other_variable;si la declaración que contiene el retorno es la última declaración en una función , returningla función en su conjunto devuelve el ID que se está editando.
Alexander Bird

32

puede usar la cláusula RETURNING en la instrucción INSERT, como lo siguiente

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 respuesta de Leonbloy es bastante completa. Solo agregaría el caso especial en el que uno necesita obtener el último valor insertado dentro de una función PL / pgSQL donde la OPCIÓN 3 no se ajusta exactamente.

Por ejemplo, si tenemos las siguientes tablas:

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

Si necesitamos insertar un registro de cliente, debemos referirnos a un registro de persona. Pero supongamos que queremos diseñar una función PL / pgSQL que inserte un nuevo registro en el cliente pero que también se encargue de insertar el nuevo registro de persona. Para eso, debemos usar una ligera variación de la OPCIÓN 3 de leonbloy:

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

Tenga en cuenta que hay dos cláusulas INTO. Por lo tanto, la función PL / pgSQL se definiría 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;

Ahora podemos insertar los nuevos datos usando:

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

o

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

Y obtenemos la identificación recién creada.

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

9

Para aquellos que necesitan obtener el registro de todos los datos, pueden agregar

returning *

hasta el final de su consulta para obtener todos los objetos, incluida la identificación.


8

Ver el siguiente ejemplo

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

Luego, para obtener la última identificación insertada, use esto para la tabla "user" seq nombre de columna "id"

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


1

Prueba esto:

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

Si esto devuelve 1 (o lo que sea el valor inicial de su secuencia), restablezca la secuencia nuevamente al valor original, pasando el indicador falso:

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

De otra manera,

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

Esto restaurará el valor de secuencia al estado original y "setval" regresará con el valor de secuencia que está buscando.


1

Postgres tiene un mecanismo incorporado para el mismo, que en la misma consulta devuelve la identificación o lo que quiera que devuelva la consulta. Aquí hay un ejemplo. Considere que tiene una tabla creada que tiene 2 columnas column1 y column2 y desea que se devuelva column1 después de cada inserción.

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

Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.