2010-05-31 33 views
216

¿Cómo puedo obtener la última identificación insertada en una tabla? En MS SQL, hay SCOPE_IDENTITY().función postgreSQL para la última ID insertada

Por favor, no aconsejan usar algo como esto:

select max(id) from table 
+0

¿por qué odias la función max? Creo que es muy simple. ¿Hay algún problema como la seguridad? –

+3

@ jeongmin.cha existe un problema si entre 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

Respuesta

409

(tl;dr: opción Goto 3: INTRODUCIR con la devolución)

Recordemos que en PostgreSQL no existe el concepto de "id" para las tablas, a sólo secuencias (que por lo general, pero no necesariamente se utilizan como valores predeterminados para sustituta claves primarias, con el pseudo-tipo SERIAL).

Si está interesado en obtener el identificador de una fila recién insertada, hay varias maneras:


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 arbitraria; en este ejemplo suponemos que la tabla persons tiene una columna id creada con el pseudo tipo SERIAL. Para evitar depender de esto y para sentirse más limpio, se puede utilizar en lugar pg_get_serial_sequence:

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

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


Opción 2: LASTVAL();

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


Tanto CURRVAL y LASTVAL son totalmente concurrente seguro.El comportamiento de la secuencia en PG está diseñado para que la 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, todavía obtengo mi valor correcto).

Sin embargo, tienen un problema potencial sutil. Si la base de datos tiene algunos TRIGGER (o REGLA) que, al insertarlos en la tabla persons, hace algunas inserciones adicionales en otras tablas ... entonces LASTVAL probablemente nos proporcione el valor incorrecto. El problema puede ocurrir incluso con CURRVAL, si las inserciones adicionales se realizan en la misma tabla persons (esto es mucho menos habitual, pero aún existe el riesgo).


Opción 3: INSERT con RETURNING

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

Ésta es la forma más limpia, eficiente y seguro para obtener el id. No tiene ninguno de los riesgos del anterior.

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


Conclusión: Si se puede, ir a la opción 3. Por otra parte, prefieren 1.

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

+15

LASTVAL() podría ser muy malo, en caso de que agregue un desencadenador/regla insertando filas sobre sí mismo en otra tabla. –

+2

'SELECT max (id)' desafortunadamente no hace el trabajo tan pronto como comienzas a eliminar filas. –

+0

@ SimonA.Eugster ¿Por qué no? – leonbloy

7

Véase 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); 

Entonces, para conseguir la última inserta Identificación utilizar esto para la mesa "usuario" nombre de la columna SEC "id"

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

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

+4

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

+2

quizás una explicación rápida de cómo usar dicha identificación devuelta? – Andrew

+0

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

22

puede utilizar la cláusula regresando en instrucción INSERT, al igual que el 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 
1

Prueba esto:

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

Si esta declaración 1 (o lo que es el start_value para su secuencia), entonces restablecer la secuencia de nuevo al valor original, pasando la bandera falsa:

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

De lo contrario,

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

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

-2

Si sólo necesita para recuperar el último ID insertado sin insertar nada se puede utilizar el siguiente ejemplo

SELECT id FROM users ORDER BY id DESC LIMIT 1; 

La esperanza puede ayudar.

+0

Op no está pidiendo este tipo de solución – ModChowdhury

11

Leonbloy's answer es bastante completo. Solo agregaría el caso especial en el que uno necesita obtener el último valor insertado desde 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 tenemos que insertar un registro de cliente hay que hacer referencia a un documento de persona. Pero digamos que queremos diseñar una función PL/pgSQL que inserte un nuevo registro en el cliente pero también se encarga de insertar el nuevo registro de persona. Para ello, debemos utilizar 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 en cláusulas. 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 obtener el ID de nueva creación .

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

Para los que necesitan para obtener el registro de todos los datos, se puede añadir

returning * 

hasta el final de la consulta para obtener el objeto de todo incluyendo el id.

Cuestiones relacionadas