sql >> Base de Datos >  >> RDS >> PostgreSQL

INSERTAR filas en varias tablas en una sola consulta, seleccionando de una tabla involucrada

Versión final

... después de más información de OP. Considere esta demostración:

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

Insertar valores - bar primero.
Sería muy útil si proporcionó datos de prueba en su pregunta como esta!

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

Establezca secuencias en los valores actuales o obtendremos infracciones de clave duplicadas:

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

Cheques:

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

Consulta:

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

Esto debería hacer lo que describe su última actualización.

La consulta asume que z es UNIQUE . Si z no es único, se vuelve más complejo. Consulte la Consulta 2 en esta respuesta relacionada para obtener una solución lista usando la función de ventana row_number() en este caso.

Además, considere reemplazar la relación 1:1 entre foo y bar con una sola mesa unida.

CTE de modificación de datos

Segunda respuesta después de más información.

Si desea agregar filas a foo y bar en una sola consulta, puede usar un CTE de modificación de datos desde 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;

Extraigo valores de foo , insértelos en bar , haz que se devuelvan junto con un bar_id generado automáticamente e inserta eso en foo . También puede utilizar cualquier otro dato.

Aquí hay una demostración funcional para jugar en sqlfiddle.

Conceptos básicos

Respuesta original con información básica antes de aclaraciones.
La forma básica es:

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

No se necesitan paréntesis. Puedes hacer lo mismo con cualquier tabla

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

Y puede unirse a la tabla en la que inserta en SELECCIONAR:

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 solo un SELECCIONAR como cualquier otro, que puede incluir la tabla en la que está insertando. Las filas se leen primero y luego se insertan.