¿Puedo usar el valor de retorno de INSERT ... RETURNING en otro INSERT?

86

¿Es posible algo como esto?

INSERT INTO Table2 (val)
VALUES ((INSERT INTO Table1 (name) VALUES ('a_title') RETURNING id));

como usar el valor de retorno como valor para insertar una fila en una segunda tabla con una referencia a la primera tabla?

Eike Cochu
fuente

Respuestas:

103

Puede hacerlo comenzando con Postgres 9.1:

with rows as (
INSERT INTO Table1 (name) VALUES ('a_title') RETURNING id
)
INSERT INTO Table2 (val)
SELECT id
FROM rows

Mientras tanto, si solo está interesado en la identificación, puede hacerlo con un disparador:

create function t1_ins_into_t2()
  returns trigger
as $$
begin
  insert into table2 (val) values (new.id);
  return new;
end;
$$ language plpgsql;

create trigger t1_ins_into_t2
  after insert on table1
for each row
execute procedure t1_ins_into_t2();
Denis de Bernardy
fuente
1
¿Cómo insertar valores al lado de la identificación de retorno? por ejemplo: INSERT INTO TABLE2 (val1, val2, val3) (1, 2, SELECT id FROM rows)
Mahmoud Hanafy
@MahmoudHanafy: reemplazar rowscon (some_query returning ...)podría funcionar hoy en día (no lo he probado).
Denis de Bernardy
2
@MahmoudHanafy: Para insertar valores al lado de la identificación de retorno, puede hacer algo como esto: INSERT INTO TABLE2 (val1, val2, val3) SELECT id, 1, 2 FROM rows
Bhindi
votado a favor! ¿Es este significado atómico si el primer inserto tiene éxito y el segundo no, qué sucede entonces?
PirateApp
2
@PirateApp ¡Recién probado! v12.4. De hecho, el primer INSERT se
revierte
57

La mejor práctica para esta situación. Utilice RETURNING … INTO.

INSERT INTO teams VALUES (...) RETURNING id INTO last_id;

Tenga en cuenta que esto es para PLPGSQL

Alexandre Assi
fuente
3
¿Es esto realmente una cosa? Ninguna parte del documento al que vinculó parece mencionar RETURNING ... INTO.
Alec
4
@Alec: encontré esta documentación en esta respuesta .
Bart Hofland
@PedroD: Lo hace.
Bart Hofland
13

En consonancia con la respuesta de Denis de Bernardy ..

Si desea que la identificación también se devuelva después y desea insertar más cosas en Table2:

with rows as (
INSERT INTO Table1 (name) VALUES ('a_title') RETURNING id
)
INSERT INTO Table2 (val, val2, val3)
SELECT id, 'val2value', 'val3value'
FROM rows
RETURNING val
Bhindi
fuente
10
DO $$
DECLARE tableId integer;
BEGIN
  INSERT INTO Table1 (name) VALUES ('a_title') RETURNING id INTO tableId;
  INSERT INTO Table2 (val) VALUES (tableId);
END $$;

Probado con psql (10.3, servidor 9.6.8)

Anders B
fuente
8

Puede utilizar la lastval()función:

Valor de retorno obtenido más recientemente con nextvalpara cualquier secuencia

Entonces algo como esto:

INSERT INTO Table1 (name) VALUES ('a_title');
INSERT INTO Table2 (val)  VALUES (lastval());

Esto funcionará bien siempre que nadie llame nextval() a ninguna otra secuencia (en la sesión actual) entre sus INSERT.

Como Denis señaló a continuación y sobre lo que advertí anteriormente, el uso lastval()puede nextval()causarle problemas si accede a otra secuencia entre sus INSERT. Esto podría suceder si hubiera un desencadenador INSERT en Table1ese llamado manualmente nextval()en una secuencia o, más probablemente, hizo un INSERT en una tabla con una clave primaria SERIALoBIGSERIAL . Si quieres ser realmente paranoico (una cosa buena, que son en realidad a conseguir que después de todo), entonces se podría usar currval(), pero que había necesidad de saber el nombre de la secuencia relevante:

INSERT INTO Table1 (name) VALUES ('a_title');
INSERT INTO Table2 (val)  VALUES (currval('Table1_id_seq'::regclass));

La secuencia generada automáticamente generalmente se nombra t_c_seqdonde testá el nombre de la tabla y cel nombre de la columna, pero siempre puede averiguarlo ingresando psqly diciendo:

=> \d table_name;

y luego mirando el valor predeterminado para la columna en cuestión, por ejemplo:

id | integer | not null default nextval('people_id_seq'::regclass)

FYI: lastval()es, más o menos, la versión PostgreSQL de MySQL LAST_INSERT_ID. Solo menciono esto porque mucha gente está más familiarizada con MySQL que con PostgreSQL, por lo que vincular lastval()a algo familiar podría aclarar las cosas.

mu es demasiado corto
fuente
2
Sin embargo, es mejor usar currval () en caso de que un disparador en la tabla1 haga inserciones posteriores.
Denis de Bernardy
@Denis: Verdadero, pero luego necesitas el nombre de la secuencia. Agregaré una pequeña actualización a ese efecto solo para cubrir todas las bases.
mu es demasiado corto
LASTVAL () y CURRVAL () funcionan para la conexión de base de datos actual, no para otras conexiones. Otros usuarios pueden actualizar la secuencia en el mismo momento, eso no cambiará sus resultados. No se preocupe por los demás, nunca cambiarán sus resultados para LASTVAL y / de CURRVAL. LASTVAL y CURRVAL no se pueden usar en absoluto cuando se usa una agrupación de conexiones sin una TRANSACCIÓN, ahí es cuando las cosas salen mal: no se controla la conexión de la base de datos.
Frank Heikens
1
@Frank: Sí, todos son específicos de la sesión, pero el problema lastvales que podría haber un INSERT basado en una secuencia a sus espaldas desde un disparador AFTER INSERT en Table1. Eso sería en la sesión actual y, presumiblemente, cambiaría lastval()cuando no lo esperas.
mu es demasiado corto
1

table_ex

id default nextval ('table_id_seq' :: regclass),

camp1 varchar

camp2 varchar

INSERT INTO table_ex(camp1,camp2) VALUES ('xxx','123') RETURNING id 
kemado77
fuente