PostgreSQL
 sql >> Base de Dados >  >> RDS >> PostgreSQL

Inserir dados em 3 tabelas por vez usando o Postgres


Use CTEs de modificação de dados :
WITH ins1 AS (
   INSERT INTO sample(firstname, lastname)
   VALUES ('fai55', 'shaggk')
-- ON     CONFLICT DO NOTHING         -- optional addition in Postgres 9.5+
   RETURNING id AS sample_id
   )
, ins2 AS (
   INSERT INTO sample1 (sample_id, adddetails)
   SELECT sample_id, 'ss' FROM ins1
   RETURNING user_id
   )
INSERT INTO sample2 (user_id, value)
SELECT user_id, 'ss2' FROM ins2;

Cada INSERT depende do anterior. SELECT em vez de VALUES garante que nada seja inserido nas tabelas subsidiárias se nenhuma linha for retornada de um INSERT anterior . (Desde o Postgres 9.5+ você pode adicionar um ON CONFLICT .)
Também é um pouco mais curto e rápido dessa forma.

Normalmente, é mais conveniente fornecer linhas de dados completas em um só lugar :
WITH data(firstname, lastname, adddetails, value) AS (
   VALUES                              -- provide data here
      ('fai55', 'shaggk', 'ss', 'ss2') -- see below
    , ('fai56', 'XXaggk', 'xx', 'xx2') -- works for multiple input rows
       --  more?                      
   )
, ins1 AS (
   INSERT INTO sample (firstname, lastname)
   SELECT firstname, lastname          -- DISTINCT? see below
   FROM   data
   -- ON     CONFLICT DO NOTHING       -- UNIQUE constraint? see below
   RETURNING firstname, lastname, id AS sample_id
   )
, ins2 AS (
   INSERT INTO sample1 (sample_id, adddetails)
   SELECT ins1.sample_id, d.adddetails
   FROM   data d
   JOIN   ins1 USING (firstname, lastname)
   RETURNING sample_id, user_id
   )
INSERT INTO sample2 (user_id, value)
SELECT ins2.user_id, d.value
FROM   data d
JOIN   ins1 USING (firstname, lastname)
JOIN   ins2 USING (sample_id);

db<>mexa aqui

Você pode precisar de conversões de tipo explícitas em um VALUES autônomo expressão - em oposição a um VALUES expressão anexada a um INSERT onde os tipos de dados são derivados da tabela de destino. Ver:
  • Como converter o tipo NULL ao atualizar várias linhas

Se várias linhas puderem vir com (firstname, lastname) , pode ser necessário dobrar duplicatas para o primeiro INSERT :
...
INSERT INTO sample (firstname, lastname)
SELECT DISTINCT firstname, lastname FROM data
...

Você pode usar uma tabela (temporária) como fonte de dados em vez do CTE data .

Provavelmente faria sentido combinar isso com uma restrição UNIQUE em (firstname, lastname) na tabela e um ON CONFLICT cláusula na consulta.

Relacionado:
  • Como usar RETURNING com ON CONFLICT no PostgreSQL?
  • O SELECT ou INSERT está em uma função propensa a condições de corrida?