Insert data in 3 tables at a time using Postgres

允我心安 提交于 2019-11-26 15:06:41

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

Each INSERT depends on the one before. SELECT instead of VALUES makes sure nothing is inserted in subsidiary tables if no row is returned from a previous INSERT. (Related: the ON CONFLICT clause in Postgres 9.5+)
It's also a bit shorter and faster this way.


Typically, it's more convenient to provide complete data rows in one place:

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

You may need explicit type casts in a separate VALUES expression (as opposed to a VALUES expression attached to an INSERT, where data types are derived from the target table.

If multiple rows can come with identical (firstname, lastname), you may need to fold duplicates for the first insert:

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

You could use a (temporary) table as data source instead of the CTE data.

Related, with more details:

Something like this

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

As the generated id from the insert into sample2 is not needed, I removed the returning clause from the last insert.

Typically, you'd use a transaction to avoid writing complicated queries.

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

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

You could also use a CTE, assuming your Postgres tag is correct. For instance:

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;

You could create an after insert trigger on the Sample table to insert into the other two tables.

The only issue i see with doing this is that you wont have a way of inserting adddetails it will always be empty or in this case ss. There is no way to insert a column into sample thats not actualy in the sample table so you cant send it along with the innital insert.

Another option would be to create a stored procedure to run your inserts.

You have the question taged mysql and postgressql which database are we talking about here?

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!