Je veux insérer des données dans 3 tables avec une seule requête.
Mes tableaux ressemblent à ci-dessous:
CREATE TABLE sample (
id bigserial PRIMARY KEY,
lastname varchar(20),
firstname varchar(20)
);
CREATE TABLE sample1(
user_id bigserial PRIMARY KEY,
sample_id bigint REFERENCES sample,
adddetails varchar(20)
);
CREATE TABLE sample2(
id bigserial PRIMARY KEY,
user_id bigint REFERENCES sample1,
value varchar(10)
);
J'obtiendrai une clé en retour pour chaque insertion et je dois insérer cette clé dans le tableau suivant.
Ma requête est:
insert into sample(firstname,lastname) values('fai55','shaggk') RETURNING id;
insert into sample1(sample_id, adddetails) values($id,'ss') RETURNING user_id;
insert into sample2(user_id, value) values($id,'ss') RETURNING id;
Mais si j'exécute des requêtes uniques, elles me renvoient simplement des valeurs et je ne peux pas les réutiliser dans la requête suivante immédiatement.
Comment y parvenir?
Utilisez CTE de modification des données :
WITH ins1 AS (
INSERT INTO sample(firstname, lastname)
VALUES ('fai55', 'shaggk')
-- ON CONFLICT DO NOTHING -- optional addition in Postgres 9.5+
RETURNING id AS user_id
)
, ins2 AS (
INSERT INTO sample1 (user_id, adddetails)
SELECT user_id, 'ss' FROM ins1
-- RETURNING user_id -- only if used in turn
)
INSERT INTO sample2 (user_id, value) -- same here
SELECT user_id, 'ss' FROM ins1;
Chaque INSERT
dépend de la précédente. SELECT
au lieu de VALUES
s'assure que rien n'est inséré dans les tables subsidiaires si aucune ligne n'est renvoyée d'un INSERT
précédent. (Connexes: les ON CONFLICT
clause dans Postgres 9.5+)
C'est aussi un peu plus court et plus rapide de cette façon.
En règle générale, il est plus pratique de fournir des lignes de données complètes en un seul endroit:
WITH data(firstname, lastname, adddetails, value) AS (
VALUES -- provide data here
(text 'fai55', text 'shaggk', text 'ss', text 'ss2') -- see below
-- more? -- works for multiple input rows
)
, ins1 AS (
INSERT INTO sample (firstname, lastname)
SELECT firstname, lastname FROM data -- DISTINCT? see below
ON CONFLICT DO NOTHING -- required UNIQUE constraint
RETURNING firstname, lastname, id AS sample_id
)
, ins2 AS (
INSERT INTO sample1 (sample_id, adddetails)
SELECT sample_id, adddetails
FROM data
JOIN ins1 USING (firstname, lastname)
RETURNING sample_id, user_id
)
INSERT INTO sample2 (user_id, value)
SELECT user_id, value
FROM data
JOIN ins1 USING (firstname, lastname)
JOIN ins2 USING (sample_id);
Vous pouvez avoir besoin de transtypages de types explicites dans une expression VALUES
distincte (par opposition à une expression VALUES
attachée à un INSERT, où les types de données sont dérivés de la table cible.
Si plusieurs lignes peuvent être accompagnées de (firstname, lastname)
, vous devrez peut-être plier les doublons pour la première insertion:
...
INSERT INTO sample (firstname, lastname)
SELECT DISTINCT firstname, lastname FROM data
...
Vous pouvez utiliser une table (temporaire) comme source de données au lieu de CTE data
.
Connexes, avec plus de détails:
Quelque chose comme ça
with first_insert as (
insert into sample(firstname,lastname)
values('fai55','shaggk')
RETURNING id
),
second_insert as (
insert into sample1( id ,adddetails)
values
( (select id from first_insert), 'ss')
RETURNING user_id
)
insert into sample2 ( id ,adddetails)
values
( (select user_id from first_insert), 'ss');
Comme l'id généré à partir de l'insert dans sample2
n'est pas nécessaire, j'ai supprimé la clause returning
de la dernière insertion.
En règle générale, vous utiliseriez une transaction pour éviter d'écrire des requêtes compliquées.
http://www.postgresql.org/docs/current/static/sql-begin.html
http://dev.mysql.com/doc/refman/5.7/en/commit.html
Vous pouvez également utiliser un CTE, en supposant que votre balise Postgres est correcte. Par exemple:
with sample_ids as (
insert into sample(firstname, lastname)
values('fai55','shaggk')
RETURNING id
), sample1_ids as (
insert into sample1(id, adddetails)
select id,'ss'
from sample_ids
RETURNING id, user_id
)
insert into sample2(id, user_id, value)
select id, user_id, 'val'
from sample1_ids
RETURNING id, user_id;
Vous pouvez créer un déclencheur après insertion sur la table d'échantillons à insérer dans les deux autres tables.
Le seul problème que je vois avec cela est que vous n'aurez pas un moyen d'insérer des adddetails, il sera toujours vide ou dans ce cas ss. Il n'y a aucun moyen d'insérer une colonne dans l'échantillon qui n'est pas réellement dans la table d'échantillons, vous ne pouvez donc pas l'envoyer avec l'insert initial.
Une autre option serait de créer une procédure stockée pour exécuter vos insertions.
Vous avez la question taged mysql et postgressql de quelle base de données parlons-nous ici?