31

Chcę wstawić dane do 3 tabel za pomocą pojedynczego zapytania.
Moi stoły wygląda jak poniżej:Wstawianie danych w 3 tabelach jednocześnie za pomocą Postgreatora:

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

dostanę klucz w zamian za każdym włożeniu i muszę wstawić ten klucz w kolejnej tabeli.
Moje zapytanie brzmi:

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; 

Ale jeśli uruchomię pojedynczych zapytań po prostu zwracają wartości do mnie i nie mogę natychmiast ponownie je w kolejnym zapytaniu.

Jak to osiągnąć?

Odpowiedz

1

Można utworzyć wyzwalacz po wstawianiu w tabeli próbek, aby wstawić go do dwóch pozostałych tabel.

Jedyny problem, jaki widzę w tym przypadku, polega na tym, że nie ma możliwości wstawiania adddetails, zawsze będzie pusty lub w tym przypadku ss. Nie ma możliwości wstawienia kolumny do próbki, która nie jest obecna w tabeli próbek, więc nie można jej przesłać wraz z wstawką innitalną.

Inną opcją byłoby utworzenie procedury składowanej w celu uruchomienia wkładek.

Masz pytanie z tytułem mysql i postgressql, z której bazy danych rozmawiamy?

8

coś takiego

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 generowanego ID z wkładki do sample2 nie jest potrzebna, usunąłem returning klauzuli z ostatniej wkładki.

60

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

Każda wkładka zależy od poprzedniego. SELECT zamiast VALUES upewnia się, że nic nie jest wstawiane do tabel pomocniczych, jeśli żaden wiersz nie jest zwracany z poprzedniej wkładki. (Powiązane: klauzula ON CONFLICT w Postgres 9.5+)
Jest również nieco krótszy i szybszy w ten sposób.


Zazwyczaj jest to bardziej wygodne do dostarczyć kompletne wierszy danych w jednym miejscu:

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

Możesz potrzebować rzuca wyraźny typ w oddzielnej ekspresji VALUES (w przeciwieństwie do ekspresji VALUES dołączone do INSERT, gdzie typy danych pochodzą z tabeli docelowej:

Jeśli wiele wierszy może pochodzić z identyfikatorem cal (firstname, lastname), może trzeba złożyć duplikaty dla pierwszej wkładki:

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

Można użyć (tymczasowe) tabelę jako źródło danych zamiast CTE data.

pokrewne, więcej szczegółów:

+0

thanx do powtórzenia mogę dodać rollout transakcji, jeśli wystąpi błąd wstawiania. Tak jak mogę – Faisal

+1

To jest pojedyncza instrukcja SQL. Można połączyć kilka zdań w jedną transakcję, ale nie można tego podzielić. Ponadto, co Denis mówi w swoim komentarzu. I dodałem kilka linków do mojej odpowiedzi. –

+0

ale czy możesz wybrać SELECT wewnątrz CTE, a następnie natychmiast po użyciu z INSERT? – mmcrae

4

Zazwyczaj, można użyć transakcji, aby uniknąć pisania skomplikowanych zapytań.

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

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

Można również użyć CTE, zakładając tagu Postgres jest prawidłowe. Na przykład:

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

thanx uzyskać transakcję w tej kwerendzie, jeśli jakakolwiek wstawka się nie powiedzie, mogę wycofać – Faisal

+0

Potem wszystko zaczynasz od nowa, po poprawieniu zapytań oczywiście, ponieważ cała transakcja (lub cte) zostałaby wycofana. Przy okazji, jeśli twoje wkładki czasami się nie sprawdzają, prawdopodobnie robisz coś źle. Jedyny przypadek, w którym uzasadnione jest niepowodzenie wstawienia, jest w scenariuszu upsert, który polega na duplikowaniu unikatowych kluczy podczas równoczesnych transakcji, a nawet wtedy można uzyskać blokadę doradczą lub blokadę stołu, jeśli trzeba poprawić punktację. –