sql >> Databasteknik >  >> RDS >> PostgreSQL

INFOGA rader i flera tabeller i en enda fråga, välj från en involverad tabell

Slutlig version

... efter lite mer info från OP. Tänk på den här demon:

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

Infoga värden - bar först.
Det skulle vara mycket nyttigt om du angav testdata i din fråga så här!

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

Ställ in sekvenser till aktuella värden eller så får vi dubbletter av nyckelöverträdelser:

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

Kontroller:

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

Fråga:

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

Detta bör göra vad din senaste uppdatering beskriver.

Frågan förutsätter att z är UNIQUE . Om z är inte unikt, det blir mer komplext. Se fråga 2 i detta relaterade svar för en färdig lösning med fönsterfunktionen row_number() i det här fallet.

Överväg också att ersätta 1:1-relationen mellan foo och bar med ett enda förenat bord.

Datamodifierande CTE

Andra svaret efter mer info.

Om du vill lägga till rader till foo och bar i en enda fråga kan du använda en datamodifierande CTE sedan 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;

Jag hämtar värden från foo , infoga dem i bar , få dem att returneras tillsammans med ett automatiskt genererat bar_id och infoga det till foo . Du kan också använda vilken annan data som helst.

Här är en fungerande demo att spela med på sqlfiddle.

Grunderna

Originalsvar med grundläggande information före förtydliganden.
Grundformuläret är:

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

Ingen parentes behövs. Du kan göra samma sak med vilken tabell som helst

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

Och du kan gå med i tabellen du infogar 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 är bara en SELECT som alla andra - som kan inkludera tabellen du infogar i. Raderna läses först och infogas sedan.



  1. Lagring av krypterad data i Postgres

  2. Hur använder man nyckelordet "som" för att alias en tabell i Oracle?

  3. SQL-ändringstabell

  4. Optimerad SQL för trädstrukturer