PostgreSQL
 sql >> Datenbank >  >> RDS >> PostgreSQL

INSERT-Zeilen in mehreren Tabellen in einer einzigen Abfrage, wobei aus einer beteiligten Tabelle ausgewählt wird

Endgültige Version

... nach einigen weiteren Informationen von OP. Sehen Sie sich diese Demo an:

-- DROP TABLE foo; DROP TABLE bar;

CREATE TEMP TABLE bar (
 id serial PRIMARY KEY  -- using a serial column!
,z  integer NOT NULL
);

CREATE TEMP TABLE foo (
 id     serial PRIMARY KEY  -- using a serial column!
,x      integer NOT NULL
,y      integer NOT NULL
,bar_id integer UNIQUE NOT NULL REFERENCES bar(id)
);

Werte einfügen - bar zuerst.
Es wäre sehr hilfreich wenn Sie in Ihrer Frage solche Testdaten angegeben haben!

INSERT INTO bar (id,z) VALUES
 (100, 7)
,(101,16)
,(102,21);

INSERT INTO foo (id, x, y, bar_id) VALUES
 (1, 3,4,100)
,(2, 9,6,101)
,(3,18,0,102);

Sequenzen auf aktuelle Werte setzen oder wir erhalten doppelte Schlüsselverletzungen:

SELECT setval('foo_id_seq', 3);
SELECT setval('bar_id_seq', 102);

Kontrollen:

-- SELECT nextval('foo_id_seq')
-- SELECT nextval('bar_id_seq')
-- SELECT * from bar;
-- SELECT * from foo;

Abfrage:

WITH a AS (
    SELECT f.x, f.y, bar_id, b.z
    FROM   foo f
    JOIN   bar b ON b.id = f.bar_id
    WHERE  x > 3
    ),b AS (
    INSERT INTO bar (z)
    SELECT z
    FROM   a
    RETURNING z, id AS bar_id
    )
INSERT INTO foo (x, y, bar_id)
SELECT a.x, a.y, b.bar_id
FROM   a
JOIN   b USING (z);

Dies sollte das tun, was Ihr letztes Update beschreibt.

Die Abfrage geht davon aus, dass z ist UNIQUE . Wenn z ist nicht einzigartig, es wird komplexer. Siehe Abfrage 2 in dieser verwandten Antwort für eine fertige Lösung mit der Fensterfunktion row_number() in diesem Fall.

Erwägen Sie auch, die 1:1-Beziehung zu ersetzen zwischen foo und bar mit einer einzigen vereinten Tabelle.

Daten ändern CTE

Zweite Antwort nach mehr Infos.

Wenn Sie Zeilen zu foo hinzufügen möchten und bar in einer einzelnen Abfrage können Sie seit PostgreSQL 9.1 einen Datenänderungs-CTE verwenden :

WITH x AS (
    INSERT INTO bar (col1, col2)
    SELECT f.col1, f.col2
    FROM   foo f
    WHERE  f.id BETWEEN 12 AND 23 -- some filter
    RETURNING col1, col2, bar_id  -- assuming bar_id is a serial column
    )
INSERT INTO foo (col1, col2, bar_id)
SELECT col1, col2, bar_id
FROM   x;

Ich ziehe Werte aus foo , fügen Sie sie in bar ein , lassen Sie sich diese zusammen mit einer automatisch generierten bar_id zurückgeben und füge das ein in foo . Sie können auch beliebige andere Daten verwenden.

Hier ist eine funktionierende Demo zum Spielen auf sqlfiddle.

Grundlagen

Ursprüngliche Antwort mit grundlegenden Informationen vor Erläuterungen.
Die Grundform lautet:

INSERT INTO foo (...)
SELECT ... FROM foo WHERE ...

Keine Klammern erforderlich. Sie können dasselbe mit jeder Tabelle tun

INSERT INTO foo (...)
SELECT ... FROM bar WHERE ...

Und Sie können sich mit der Tabelle verbinden, in die Sie in SELECT:

einfügen
INSERT INTO foo (...)
SELECT f.col1, f.col2, .. , b.bar_id
FROM   foo f
JOIN   bar b USING (foo_id);  -- present in foo and bar

Es ist nur ein SELECT wie jedes andere - das kann die Tabelle enthalten, in die Sie einfügen. Die Zeilen werden zuerst gelesen und dann eingefügt.