Posso usar o valor de retorno de INSERT… RETURNING em outro INSERT?

87

Algo assim é possível?

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

como usar o valor de retorno como valor para inserir uma linha em uma segunda tabela com uma referência à primeira tabela?

Eike Cochu
fonte

Respostas:

106

Você pode fazer isso começando com Postgres 9.1:

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

Enquanto isso, se você estiver interessado apenas no id, poderá fazer isso com um gatilho:

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
fonte
1
Como inserir valores ao lado do id de retorno? por exemplo: INSERT INTO TABLE2 (val1, val2, val3) (1, 2, SELECT id FROM rows)
Mahmoud Hanafy
@MahmoudHanafy: substituir rowspor (some_query returning ...)pode funcionar hoje em dia (não tentei).
Denis de Bernardy
2
@MahmoudHanafy: Para inserir valores ao lado do id de retorno, você pode fazer algo assim: INSERT INTO TABLE2 (val1, val2, val3) SELECT id, 1, 2 FROM rows
Bhindi
voto positivo! Este é o significado atômico se a primeira inserção for bem-sucedida e a segunda não, o que acontece então?
PirateApp
2
@PirateApp Acabei de testar! v12.4. O primeiro INSERT é de fato revertido se o segundo falhar, mas a série / incremento automático do primeiro INSERT é ignorado
Madacol
58

A melhor prática para esta situação. Use RETURNING … INTO.

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

Observe que isso é para PLPGSQL

Alexandre Assi
fonte
3
Isso é realmente uma coisa? Nenhuma parte do documento ao qual você vinculou parece mencionar RETURNING ... INTO.
Alec
4
@Alec: Eu encontrei esta documentação em esta resposta .
Bart Hofland
@PedroD: Sim.
Bart Hofland
13

Em linha com a resposta dada por Denis de Bernardy ..

Se você deseja que o id seja retornado posteriormente também e deseja inserir mais coisas na Tabela 2:

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
fonte
10
DO $$
DECLARE tableId integer;
BEGIN
  INSERT INTO Table1 (name) VALUES ('a_title') RETURNING id INTO tableId;
  INSERT INTO Table2 (val) VALUES (tableId);
END $$;

Testado com psql (10.3, servidor 9.6.8)

Anders B
fonte
8

Você pode usar a lastval()função:

Valor de retorno obtido mais recentemente com nextvalpara qualquer sequência

Então, algo assim:

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

Isso funcionará bem, desde que ninguém chame nextval()em qualquer outra sequência (na sessão atual) entre seus INSERTs.

Como Denis observou abaixo e eu avisei acima, usar lastval()pode causar problemas se outra sequência for acessada usando nextval()entre seus INSERTs. Isso poderia acontecer se houvesse um gatilho INSERT Table1chamado manualmente nextval()em uma sequência ou, mais provavelmente, se houvesse um INSERT em uma tabela com uma chave primária SERIALouBIGSERIAL . Se você quiser ser realmente paranóico (uma coisa boa, eles realmente são você para te pegar, afinal), você poderia usar, currval()mas você precisa saber o nome da sequência relevante:

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

A sequência gerada automaticamente é geralmente nomeada t_c_seqonde té o nome da tabela e cé o nome da coluna, mas você sempre pode descobrir entrando psqle dizendo:

=> \d table_name;

e, em seguida, olhar para o valor padrão da coluna em questão, por exemplo:

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

FYI: lastval()é, mais ou menos, a versão PostgreSQL do MySQL LAST_INSERT_ID. Menciono isso apenas porque muitas pessoas estão mais familiarizadas com o MySQL do que com o PostgreSQL, portanto, vincular lastval()a algo familiar pode esclarecer as coisas.

mu é muito curto
fonte
2
É melhor usar currval (), no caso de um gatilho na tabela 1 fazer inserções subsequentes.
Denis de Bernardy
@Denis: Verdadeiro, mas você precisa do nome da sequência. Vou adicionar uma pequena atualização para esse efeito apenas para cobrir todas as bases.
mu é muito curto
LASTVAL () e CURRVAL () funcionam para a conexão de banco de dados atual, não para outras conexões. Outros usuários podem atualizar a sequência ao mesmo tempo, o que não mudará seus resultados. Não se preocupe com os outros, eles nunca irão alterar seus resultados para LASTVAL e / de CURRVAL. LASTVAL e CURRVAL não podem ser usados ​​ao usar um pool de conexão sem uma TRANSACTION, é quando as coisas dão errado: você não controla a conexão do banco de dados.
Frank Heikens
1
@Frank: Sim, eles são todos específicos da sessão, mas o problema lastvalé que pode haver um INSERT baseado em sequência nas suas costas de um gatilho AFTER INSERT na Tabela1. Isso seria na sessão atual e, provavelmente, mudaria lastval()quando você não estivesse esperando.
mu é muito curto
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
fonte