PostgreSQL
 sql >> Database >  >> RDS >> PostgreSQL

INSERT righe in più tabelle in una singola query, selezionando da una tabella coinvolta

Versione finale

... dopo qualche informazione in più da OP. Considera questa 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)
);

Inserisci valori - bar primo.
Sarebbe molto utile se hai fornito i dati del test nella tua domanda in questo modo!

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

Imposta le sequenze sui valori correnti o otteniamo violazioni delle chiavi duplicate:

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

Controlli:

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

Domanda:

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

Questo dovrebbe fare ciò che descrive il tuo ultimo aggiornamento.

La query presuppone che z è UNIQUE . Se z non è unico, diventa più complesso. Fare riferimento alla query 2 in questa risposta correlata per una soluzione pronta utilizzando la funzione della finestra row_number() in questo caso.

Inoltre, considera la possibilità di sostituire la relazione 1:1 tra foo e bar con un unico tavolo unito.

Modifica dati CTE

Seconda risposta dopo maggiori informazioni.

Se vuoi aggiungere righe a foo e bar in una singola query, puoi utilizzare un CTE di modifica dei dati a partire da 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;

Traggo valori da foo , inseriscili in bar , falli restituire insieme a un bar_id generato automaticamente e inserisci quello in foo . Puoi utilizzare anche qualsiasi altro dato.

Ecco una demo funzionante con cui giocare su sqlfiddle.

Nozioni di base

Risposta originale con informazioni di base prima dei chiarimenti.
Il modulo di base è:

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

Nessuna parentesi necessaria. Puoi fare lo stesso con qualsiasi tabella

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

E puoi unirti al tavolo in cui inserisci in 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

È solo un SELECT come un altro, che può includere la tabella in cui stai inserendo. Le righe vengono prima lette, quindi inserite.