Endelig version
... efter lidt mere info fra OP. Overvej denne demo:
-- 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)
);
Indsæt værdier - bar
først.
Det ville være meget nyttigt hvis du har angivet testdata i dit spørgsmål som dette!
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);
Indstil sekvenser til aktuelle værdier, ellers får vi dublerede nøgleovertrædelser:
SELECT setval('foo_id_seq', 3);
SELECT setval('bar_id_seq', 102);
Tjek:
-- SELECT nextval('foo_id_seq')
-- SELECT nextval('bar_id_seq')
-- SELECT * from bar;
-- SELECT * from foo;
Forespørgsel:
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);
Dette burde gøre, hvad din sidste opdatering beskriver.
Forespørgslen antager, at z
er UNIQUE
. Hvis z
er ikke unikt, det bliver mere komplekst. Se forespørgsel 2 i dette relaterede svar for en klar løsning ved hjælp af vinduesfunktionen row_number()
i dette tilfælde.
Overvej også at erstatte 1:1-relationen mellem foo
og bar
med et enkelt samlet bord.
Datamodificerende CTE
Andet svar efter mere info.
Hvis du vil tilføje rækker til foo
og bar
i en enkelt forespørgsel kan du bruge en datamodificerende CTE siden PostgreSQL 9.1 :
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;
Jeg trækker værdier fra foo
, indsæt dem i bar
, få dem returneret sammen med en autogenereret bar_id
og indsæt det ind i foo
. Du kan også bruge alle andre data.
Her er en fungerende demo at spille med på sqlfiddle.
Grundlæggende
Originalt svar med grundlæggende oplysninger før afklaringer.
Grundformularen er:
INSERT INTO foo (...)
SELECT ... FROM foo WHERE ...
Ingen parentes nødvendig. Du kan gøre det samme med enhver tabel
INSERT INTO foo (...)
SELECT ... FROM bar WHERE ...
Og du kan tilslutte dig den tabel, du indsætter i i SELECT:
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
Det er bare en SELECT som enhver anden - der kan inkludere den tabel, du indsætter i. Rækkerne læses først og indsættes derefter.