PostgreSQL
 sql >> Base de données >  >> RDS >> PostgreSQL

INSÉRER des lignes dans plusieurs tables dans une seule requête, en sélectionnant dans une table impliquée

Version finale

... après quelques informations supplémentaires d'OP. Considérez cette démo :

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

Insérer des valeurs - bar d'abord.
Ce serait très utile si vous avez fourni des données de test dans votre question comme celle-ci !

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

Définissez les séquences sur les valeurs actuelles ou nous obtenons des violations de clé en double :

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

Chèques :

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

Requête :

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

Cela devrait faire ce que votre dernière mise à jour décrit.

La requête suppose que z est UNIQUE . Si z n'est pas unique, il devient plus complexe. Reportez-vous à la requête 2 dans cette réponse connexe pour une solution prête à l'aide de la fonction de fenêtre row_number() dans ce cas.

Envisagez également de remplacer la relation 1:1 entre foo et bar avec une seule table unie.

CTE modifiant les données

Deuxième réponse après plus d'informations.

Si vous souhaitez ajouter des lignes à foo et bar en une seule requête, vous pouvez utiliser un CTE de modification de données depuis 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;

Je tire des valeurs de foo , insérez-les dans bar , les renvoyer avec un bar_id généré automatiquement et insérez ça dans foo . Vous pouvez également utiliser d'autres données.

Voici une démo fonctionnelle avec laquelle jouer sur sqlfiddle.

Bases

Réponse originale avec des informations de base avant les clarifications.
La forme de base est :

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

Aucune parenthèse n'est nécessaire. Vous pouvez faire la même chose avec n'importe quel tableau

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

Et vous pouvez vous joindre à la table que vous insérez dans le 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

C'est juste un SELECT comme un autre - qui peut inclure la table dans laquelle vous insérez. Les lignes sont d'abord lues, puis insérées.