För tusentals poster
1. Skapa en tillfällig tabell med inmatningsrader, som består av dina värden $1
, $2
, $3
. Det snabbaste sättet att ladda upp är COPY
- eller \copy
metakommando för psql
om data inte finns på samma maskin. Låt oss anta att den här tabellen:
CREATE TEMP TABLE tmp(id int PRIMARY KEY, val1 text, val2 text);
Jag lade till en PK-begränsning, som är helt valfri, men den ser till att vi har att göra med unika int-värden som inte är null. Om du kan garantera indata behöver du inte begränsningen.
2. Kedja dina kommandon med datamodifierande CTE:er. Som vi har fastställt under din föregående fråga , det finns inga tävlingsförhållanden att ta hand om i just denna operation.
WITH ins1 AS (
INSERT INTO table1 AS t1 (id, val1, val2)
SELECT id, val1, val2 FROM tmp ON CONFLICT DO NOTHING
RETURNING t1.id, t1.val1, t1.val2 -- only actually inserted rows returned
)
, ins2 AS (
INSERT INTO table2 (table1_id, val1)
SELECT id, val1 FROM ins1
)
UPDATE table3 t3
SET val2 = i.val2
, time = now()
FROM ins1 i
WHERE t3.table1_id = i.id;
Steg 1. och 2. måste köras i samma session (inte nödvändigtvis samma transaktion), eftersom omfattningen av tillfälliga tabeller är bunden till samma session.
Notera, UPDATE
beror bara på den första INSERT
, framgång för den andra INSERT
är garanterad, eftersom det inte finns någon ON CONFLICT DO NOTHING
och hela operationen skulle återställas om det finns någon konflikt i den andra INSERT
.
Relaterat:
För bara ett par poster
Det finns olika alternativ hur. Din idé att skicka en JSON-array till en funktion är en av dem. Om objekt matchar måltabellen kan du använda json_populate_recordset()
i en enda INSERT
fråga. Eller använd bara INSERT
(som förberett uttalande) utan funktionsomslag.
INSERT INTO target_tbl -- it's ok to omit target columns here
SELECT *
FROM json_populate_recordset(null::target_tbl, -- use same table type
json '[{ "id": "1", "val1": "1-val1", "val2": "1-val2" },
{ "id": "2", "val1": "2-val1", "val2": "2-val2" },
{ "id": "3", "val1": "3-val1", "val2": "3-val2" },
{ "id": "4", "val1": "4-val1", "val2": "4-val2" }]');
För bara en handfull kolumner kan du också skicka en array för varje kolumn och gå igenom dem parallellt. Du kan göra detta med en enkel slinga på arrayindexet. Sedan Postgres 9.4 finns även den praktiska unnest()
med flera parametrar för att göra allt i en enda fråga:
Den bästa lösningen beror på vilket dataformat du har .