Kann ich den Rückgabewert von INSERT… RETURNING in einem anderen INSERT verwenden?

86

Ist so etwas möglich?

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

Möchten Sie den Rückgabewert als Wert verwenden, um eine Zeile in eine zweite Tabelle mit einem Verweis auf die erste Tabelle einzufügen?

Eike Cochu
quelle

Antworten:

103

Sie können dies ab Postgres 9.1 tun:

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

Wenn Sie in der Zwischenzeit nur an der ID interessiert sind, können Sie dies mit einem Auslöser tun:

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
quelle
1
Wie füge ich Werte neben der zurückkehrenden ID ein? Zum Beispiel: INSERT IN TABLE2 (val1, val2, val3) (1, 2, SELECT id FROM rows)
Mahmoud Hanafy
@ MahmoudHanafy: Das Ersetzen rowsdurch (some_query returning ...)könnte heutzutage funktionieren (habe es nicht versucht).
Denis de Bernardy
2
@ MahmoudHanafy: Um Werte neben der zurückkehrenden ID einzufügen, können Sie Folgendes tun: INSERT IN TABLE2 (val1, val2, val3) SELECT id, 1, 2 FROM Zeilen
Bhindi
upvoted! Ist diese atomare Bedeutung, wenn die erste Einfügung erfolgreich ist und die zweite nicht, was dann passiert?
PirateApp
2
@ PirateApp Gerade getestet! v12.4. Das erste INSERT wird zwar zurückgesetzt, wenn das zweite fehlschlägt, aber die Serie / Autoinkrementierung des ersten INSERT wird übersprungen
Madacol
57

Die beste Vorgehensweise für diese Situation. Verwenden Sie RETURNING … INTO.

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

Beachten Sie, dass dies für PLPGSQL gilt

Alexandre Assi
quelle
3
Ist das eigentlich eine Sache? Kein Teil des Dokuments, auf das Sie verlinkt haben, scheint dies zu erwähnen RETURNING ... INTO.
Alec
4
@Alec: Ich habe diese Dokumentation in dieser Antwort gefunden .
Bart Hofland
@ PedroD: Das tut es.
Bart Hofland
13

In Übereinstimmung mit der Antwort von Denis de Bernardy ..

Wenn Sie möchten, dass die ID auch danach zurückgegeben wird, und weitere Elemente in Tabelle 2 einfügen möchten:

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

Getestet mit psql (10.3, Server 9.6.8)

Anders B.
quelle
8

Sie können die lastval()Funktion verwenden:

Rückgabewert, der zuletzt mit nextvalfür eine beliebige Sequenz erhalten wurde

Also so etwas wie das:

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

Dies funktioniert nextval()einwandfrei, solange niemand eine andere Sequenz (in der aktuellen Sitzung) zwischen Ihren INSERTs aufruft.

Wie Denis unten bemerkt und ich oben gewarnt habe, lastval()kann die Verwendung von Sie in Schwierigkeiten geraten, wenn nextval()zwischen Ihren INSERTs auf eine andere Sequenz zugegriffen wird. Dies kann passieren, wenn ein INSERT-Trigger für Table1eine manuell aufgerufene nextval()Sequenz vorhanden ist oder wenn ein INSERT für eine Tabelle mit einem SERIALoder einemBIGSERIAL Primärschlüssel ausgeführt wird. Wenn Sie wirklich paranoid sein wollen (eine gute Sache, sie sind es wirklich, um Sie zu bekommen), dann könnten Sie verwenden, currval()aber Sie müssten den Namen der relevanten Sequenz kennen:

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

Die automatisch generierte Sequenz wird normalerweise benannt, t_c_seqwobei tder Tabellenname und cder Spaltenname angegeben sind. Sie können dies jedoch jederzeit herausfinden, indem Sie Folgendes psqlsagen:

=> \d table_name;

und dann den Standardwert für die betreffende Spalte betrachten, zum Beispiel:

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

Zu Ihrer Information: lastval()ist mehr oder weniger die PostgreSQL-Version von MySQL LAST_INSERT_ID. Ich erwähne dies nur, weil viele Leute mit MySQL besser vertraut sind als mit PostgreSQL, so dass das Verknüpfen lastval()mit etwas Bekanntem die Dinge klären könnte.

mu ist zu kurz
quelle
2
Verwenden Sie jedoch besser currval (), falls ein Trigger in Tabelle1 nachfolgende Einfügungen ausführt.
Denis de Bernardy
@Denis: Stimmt, aber dann brauchst du den Namen der Sequenz. Ich werde ein kleines Update zu diesem Effekt hinzufügen, um alle Grundlagen abzudecken.
Mu ist zu kurz
LASTVAL () und CURRVAL () funktionieren beide für die aktuelle Datenbankverbindung, nicht für andere Verbindungen. Andere Benutzer können die Sequenz zur gleichen Zeit aktualisieren, wodurch sich Ihre Ergebnisse nicht ändern. Machen Sie sich keine Sorgen um andere, sie werden Ihre Ergebnisse für LASTVAL und / oder CURRVAL niemals ändern. LASTVAL und CURRVAL können überhaupt nicht verwendet werden, wenn ein Verbindungspool ohne TRANSAKTION verwendet wird. Dann geht etwas schief: Sie steuern die Datenbankverbindung nicht.
Frank Heikens
1
@Frank: Ja, sie sind alle sitzungsspezifisch, aber das Problem lastvalist, dass sich hinter Ihrem Rücken möglicherweise ein sequenzbasiertes INSERT von einem AFTER INSERT-Trigger in Tabelle 1 befindet. Das wäre in der aktuellen Sitzung und würde sich vermutlich ändern, lastval()wenn Sie es nicht erwarten.
Mu ist zu kurz
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
quelle