PostgreSQL
 sql >> Base de Dados >  >> RDS >> PostgreSQL

INSERT linhas em várias tabelas em uma única consulta, selecionando de uma tabela envolvida

Versão final


... depois de mais algumas informações do OP. Considere esta demonstração:
-- 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)
);

Inserir valores - bar primeiro.
Seria muito útil se você forneceu dados de teste em sua pergunta assim!
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);

Defina as sequências para os valores atuais ou obtemos violações de chave duplicadas:
SELECT setval('foo_id_seq', 3);
SELECT setval('bar_id_seq', 102);

Verificações:
-- SELECT nextval('foo_id_seq')
-- SELECT nextval('bar_id_seq')
-- SELECT * from bar;
-- SELECT * from foo;

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

Isso deve fazer o que sua última atualização descreve.

A consulta assume que z é UNIQUE . Se z não é único, fica mais complexo. Consulte a Consulta 2 nesta resposta relacionada para uma solução pronta usando a função de janela row_number() nesse caso.

Além disso, considere substituir a relação 1:1 entre foo e bar com uma única mesa unida.

CTE de modificação de dados


Segunda resposta após mais informações.

Se você deseja adicionar linhas a foo e bar em uma única consulta, você pode usar um CTE de modificação de dados desde o 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;

Eu desenho valores de foo , insira-os em bar , devolva-os junto com um bar_id gerado automaticamente e insira isso em foo . Você também pode usar outros dados.

Aqui está uma demonstração de trabalho para brincar no sqlfiddle.

Noções básicas


Resposta original com informações básicas antes dos esclarecimentos.
A forma básica é:
INSERT INTO foo (...)
SELECT ... FROM foo WHERE ...

Não é necessário parênteses. Você pode fazer o mesmo com qualquer tabela
INSERT INTO foo (...)
SELECT ... FROM bar WHERE ...

E você pode juntar-se à tabela que você insere no 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

É apenas um SELECT como qualquer outro - que pode incluir a tabela que você está inserindo. As linhas são lidas primeiro e depois inseridas.