31

Voglio inserire i dati in 3 tabelle con una singola query.
mie tabelle si presenta come di seguito:Inserire i dati in 3 tabelle alla volta utilizzando Postgres

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

mi metterò una chiave in cambio di ogni inserimento e ho bisogno di inserire la chiave nella tabella successiva.
La mia domanda è:

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; 

Ma se corro query singole hanno solo valori restituiti a me e io non li può riutilizzare nella prossima interrogazione immediatamente.

Come raggiungere questo obiettivo?

risposta

1

È possibile creare un trigger dopo l'inserimento nella tabella Esempio da inserire nelle altre due tabelle.

L'unico problema che vedo con questo è che non si ha un modo di inserire i file aggiunti che sarà sempre vuoto o in questo caso ss. Non c'è modo di inserire una colonna in un campione che non sia effettivamente nella tabella di esempio in modo da non poterla inviare insieme all'inserto innital.

Un'altra opzione potrebbe essere quella di creare una stored procedure per eseguire i propri inserimenti.

Hai la domanda su mysql e postgressql di quale database stiamo parlando qui?

8

Qualcosa di simile

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

Come l'id generato dalla inserto in sample2 non è necessario, ho rimosso la clausola returning dall'ultimo inserto.

60

Uso data-modifying CTEs:

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; 

Ogni inserto dipende quello precedente. SELECT anziché VALUES si assicura che non venga inserito nulla nelle tabelle secondarie se non viene restituita alcuna riga dall'inserimento precedente. (Correlato: la clausola ON CONFLICT in Postgres 9.5+)
È anche un po 'più breve e più veloce in quel modo.


In genere, è più conveniente per fornire righe di dati completi in un unico luogo:

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

Potrebbe essere necessario tipo esplicito getta in un separato VALUES espressione (al contrario di un'espressione VALUES allegato a un INSERT, in cui i tipi di dati sono derivati ​​dalla tabella di destinazione

Se più file possono venire con identi cal (firstname, lastname), potrebbe essere necessario piegare i duplicati per il primo inserimento:

... 
INSERT INTO sample (firstname, lastname) 
SELECT DISTINCT firstname, lastname FROM data 
... 

Si potrebbe utilizzare una tabella (temporanea) come sorgente di dati al posto del CTE data.

correlati, con maggiori dettagli:

+0

grazie per la riproduzione È possibile aggiungere rotolo di transazione se qualsiasi falliscono inserimento avviene .yes come posso – Faisal

+1

Si tratta di una singola istruzione SQL. È possibile raggruppare più istruzioni in un'unica transazione, ma non è possibile dividerla. Inoltre, cosa dice Denis nel suo commento. E ho aggiunto alcuni link alla mia risposta. –

+0

ma puoi avere un SELECT all'interno del CTE, e subito dopo usarlo con un INSERT? – mmcrae

4

In genere, devi usare una transazione per evitare di scrivere query complesse.

http://www.postgresql.org/docs/current/static/sql-begin.html

http://dev.mysql.com/doc/refman/5.7/en/commit.html

Si potrebbe anche usare un CTE, supponendo che il tag Postgres è corretto. Per esempio:

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; 
+0

grazie come vorrei raggiungi la transazione in questa query se qualche inserto fallisce potrei fare il rollback – Faisal

+0

Poi ricomincia tutto da capo, dopo aver corretto le query ovviamente, poiché l'intera transazione (o la cte) verrebbe ripristinata. A proposito, se i tuoi inserimenti falliscono di tanto in tanto, probabilmente stai facendo qualcosa di sbagliato. L'unico caso in cui è ragionevole che un inserto fallisca è uno scenario di upsert che viene eseguito in duplicate chiavi univoche durante le transazioni simultanee e anche in questo caso è possibile ottenere un blocco di avviso o un blocco di tabella se è necessario rendere le cose a prova di proiettile. –