Posso utilizzare il valore di ritorno di INSERT ... RETURNING in un altro INSERT?


87

È possibile qualcosa di simile?

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

come usare il valore di ritorno come valore per inserire una riga in una seconda tabella con un riferimento alla prima tabella?

Risposte:


106

Puoi farlo a partire da Postgres 9.1:

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

Nel frattempo, se sei interessato solo all'id, puoi farlo con un trigger:

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

1
Come inserire valori accanto all'ID restituito? ad esempio: INSERT INTO TABLE2 (val1, val2, val3) (1, 2, SELECT id FROM rows)
Mahmoud Hanafy

@MahmoudHanafy: la sostituzione rowscon (some_query returning ...)potrebbe funzionare al giorno d'oggi (non ho provato).
Denis de Bernardy

2
@MahmoudHanafy: per inserire valori accanto all'ID restituito puoi fare qualcosa del genere: INSERT INTO TABLE2 (val1, val2, val3) SELECT id, 1, 2 FROM rows
Bhindi

votato! è questo significato atomico se il primo inserimento ha successo e il secondo no, cosa succede allora?
PirateApp

2
@PirateApp Appena testato! v12.4. Il primo INSERT viene effettivamente eseguito il rollback se il secondo fallisce, ma la serie / autoincremento del primo INSERT viene saltata
Madacol


13

In linea con la risposta data da Denis de Bernardy ..

Se vuoi che l'id venga restituito anche in seguito e vuoi inserire più cose in 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

10
DO $$
DECLARE tableId integer;
BEGIN
  INSERT INTO Table1 (name) VALUES ('a_title') RETURNING id INTO tableId;
  INSERT INTO Table2 (val) VALUES (tableId);
END $$;

Testato con psql (10.3, server 9.6.8)


8

Puoi usare la lastval()funzione:

Restituisce il valore ottenuto più di recente con nextvalper qualsiasi sequenza

Quindi qualcosa del genere:

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

Questo funzionerà bene finché nessuno chiama nextval() su un'altra sequenza (nella sessione corrente) tra i tuoi INSERT.

Come Denis ha notato di seguito e ho avvertito sopra, l'uso lastval()può metterti nei guai se si accede a un'altra sequenza usando nextval()tra i tuoi INSERT. Ciò potrebbe accadere se ci fosse un trigger INSERT su Table1quello chiamato manualmente nextval()su una sequenza o, più probabilmente, se facesse un INSERT su una tabella con una chiave primaria SERIALoBIGSERIAL . Se si vuole essere veramente paranoico (una buona cosa, che in realtà sono di ottenere voi, dopo tutto), allora si potrebbe utilizzare currval(), ma avresti bisogno di conoscere il nome della sequenza in questione:

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

La sequenza generata automaticamente è solitamente denominata t_c_seqdove tè il nome della tabella ed cè il nome della colonna, ma puoi sempre scoprirlo entrando psqle dicendo:

=> \d table_name;

e quindi guardando il valore predefinito per la colonna in questione, ad esempio:

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

FYI: lastval()è, più o meno, la versione PostgreSQL di MySQLLAST_INSERT_ID . Lo dico solo perché molte persone hanno più familiarità con MySQL che con PostgreSQL, quindi collegarsi lastval()a qualcosa di familiare potrebbe chiarire le cose.


2
Meglio usare currval (), tuttavia, nel caso in cui un trigger su table1 esegua inserimenti successivi.
Denis de Bernardy

@ Denis: Vero, ma allora hai bisogno del nome della sequenza. Aggiungerò un piccolo aggiornamento a quell'effetto solo per coprire tutte le basi.
mu è troppo breve

LASTVAL () e CURRVAL () funzionano entrambi per la connessione al database corrente, non per altre connessioni. Altri utenti possono aggiornare la sequenza allo stesso tempo, ciò non cambierà i risultati. Non preoccuparti per gli altri, non cambieranno mai i tuoi risultati per LASTVAL e / di CURRVAL. LASTVAL e CURRVAL non possono essere utilizzati affatto quando si utilizza un connectionpool senza TRANSACTION, ecco quando le cose vanno storte: non si controlla la connessione al database.
Frank Heikens

1
@Frank: Sì, sono tutti specifici della sessione, ma il problema lastvalè che potrebbe esserci un INSERT basato sulla sequenza dietro la schiena da un trigger AFTER INSERT su Table1. Sarebbe nella sessione corrente e, presumibilmente, cambierebbe lastval()quando non te lo aspetti.
mu è troppo breve

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 
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.