9.5 a novější:
Podpora PostgreSQL 9.5 a novější INSERT ... ON CONFLICT (key) DO UPDATE
(a ON CONFLICT (key) DO NOTHING
), tj. upsert.
Porovnejte s ON DUPLICATE KEY UPDATE
.
Rychlé vysvětlení.
Použití viz manuál - konkrétně conflict_action klauzule v syntaktickém diagramu a vysvětlující text.
Na rozdíl od řešení pro 9.4 a starší, která jsou uvedena níže, tato funkce funguje s více konfliktními řádky a nevyžaduje výhradní zamykání ani opakovací smyčku.
Potvrzení přidání funkce je zde a diskuse o jejím vývoji je zde.
Pokud používáte verzi 9.5 a nepotřebujete být zpětně kompatibilní, můžete nyní přestat číst .
9.4 a starší:
PostgreSQL nemá žádný vestavěný UPSERT
(nebo MERGE
) zařízení a dělat to efektivně při současném používání je velmi obtížné.
Tento článek podrobně popisuje problém.
Obecně si musíte vybrat ze dvou možností:
- Jednotlivé operace vložení/aktualizace ve smyčce opakování; nebo
- Zamknutí tabulky a hromadné sloučení
Smyčka opakování jednotlivých řádků
Použití jednotlivých upserts řádku ve smyčce opakování je rozumnou možností, pokud chcete, aby se mnoho připojení současně snažilo provádět vkládání.
Dokumentace PostgreSQL obsahuje užitečný postup, který vám to umožní ve smyčce uvnitř databáze. Na rozdíl od většiny naivních řešení chrání před ztracenými aktualizacemi a vkládáním ras. Bude fungovat pouze v režimu READ COMMITTED
a je bezpečný pouze v případě, že je to jediná věc, kterou v transakci uděláte. Funkce nebude správně fungovat, pokud spouštěče nebo sekundární jedinečné klíče způsobí jedinečná porušení.
Tato strategie je velmi neefektivní. Kdykoli je to praktické, měli byste si zařadit práci do fronty a místo toho provést hromadný upsert, jak je popsáno níže.
Mnoho pokusů o řešení tohoto problému nezohledňuje vrácení zpět, takže jejich výsledkem jsou neúplné aktualizace. Dvě transakce spolu závodí; jeden z nich úspěšně INSERT
s; druhý dostane chybu duplicitního klíče a provede UPDATE
namísto. UPDATE
bloky čekající na INSERT
vrátit zpět nebo potvrdit. Když se vrátí zpět, zobrazí se UPDATE
podmínka re-check odpovídá nule řádků, takže i když UPDATE
zavazuje, že ve skutečnosti neprovedl upsert, který jste očekávali. Musíte zkontrolovat počet řádků výsledků a v případě potřeby to zkusit znovu.
Některá pokusy o řešení také nezohledňují SELECT rasy. Pokud vyzkoušíte zřejmé a jednoduché:
-- THIS IS WRONG. DO NOT COPY IT. It's an EXAMPLE.
BEGIN;
UPDATE testtable
SET somedata = 'blah'
WHERE id = 2;
-- Remember, this is WRONG. Do NOT COPY IT.
INSERT INTO testtable (id, somedata)
SELECT 2, 'blah'
WHERE NOT EXISTS (SELECT 1 FROM testtable WHERE testtable.id = 2);
COMMIT;
potom, když běží dva najednou, existuje několik režimů selhání. Jedním z nich je již diskutovaný problém s opětovnou kontrolou aktualizací. Další je, kde obě UPDATE
současně se spárováním nulových řádků a pokračováním. Pak oba provedou EXISTS
test, ke kterému dojde před INSERT
. Oba dostanou nula řádků, takže oba provedou INSERT
. Jeden selže s chybou duplicitního klíče.
To je důvod, proč potřebujete opakovací smyčku. Možná si myslíte, že můžete zabránit chybám duplicitních klíčů nebo ztraceným aktualizacím pomocí chytrého SQL, ale nemůžete. Musíte zkontrolovat počty řádků nebo ošetřit duplicitní klíčové chyby (v závislosti na zvoleném přístupu) a zkusit to znovu.
Nevytvářejte pro to své vlastní řešení. Stejně jako u řazení zpráv do fronty je to pravděpodobně špatně.
Hromadné upsert se zámkem
Někdy chcete udělat hromadný upsert, kde máte novou datovou sadu, kterou chcete sloučit do starší existující datové sady. To je obrovské účinnější než samostatné nástavce a měly by být preferovány, kdykoli je to praktické.
V tomto případě obvykle postupujte podle následujícího postupu:
-
CREATE
aTEMPORARY
tabulka -
COPY
nebo hromadně vložte nová data do dočasné tabulky -
LOCK
cílovou tabulkuIN EXCLUSIVE MODE
. To umožňuje ostatním transakcímSELECT
, ale neprovádějte žádné změny v tabulce. -
Proveďte
UPDATE ... FROM
existujících záznamů pomocí hodnot v dočasné tabulce; -
Proveďte
INSERT
řádků, které v cílové tabulce ještě neexistují; -
COMMIT
, uvolníte zámek.
Například pro příklad uvedený v otázce pomocí INSERT
s více hodnotami k naplnění dočasné tabulky:
BEGIN;
CREATE TEMPORARY TABLE newvals(id integer, somedata text);
INSERT INTO newvals(id, somedata) VALUES (2, 'Joe'), (3, 'Alan');
LOCK TABLE testtable IN EXCLUSIVE MODE;
UPDATE testtable
SET somedata = newvals.somedata
FROM newvals
WHERE newvals.id = testtable.id;
INSERT INTO testtable
SELECT newvals.id, newvals.somedata
FROM newvals
LEFT OUTER JOIN testtable ON (testtable.id = newvals.id)
WHERE testtable.id IS NULL;
COMMIT;
Související čtení
- UPSERT wiki stránku
- UPSERTismy v Postgres
- Vložit při duplicitní aktualizaci v PostgreSQL?
- http://petereisentraut.blogspot.com/2010/05/merge-syntax.html
- Odeslat transakci
- Je SELECT nebo INSERT ve funkci náchylný k závodům?
- SQL
MERGE
na PostgreSQL wiki - Nejčastější způsob implementace UPSERT v Postgresql v současnosti
A co MERGE
?
MERGE
standardu SQL ve skutečnosti má špatně definovanou sémantiku souběžnosti a není vhodný pro upserting bez předchozího zamknutí tabulky.
Je to opravdu užitečný příkaz OLAP pro slučování dat, ale ve skutečnosti to není užitečné řešení pro upsert bezpečný pro souběžnost. Existuje mnoho rad pro lidi, kteří používají jiné DBMS, aby používali MERGE
pro upserts, ale ve skutečnosti je to špatně.
Další databáze:
INSERT ... ON DUPLICATE KEY UPDATE
v MySQLMERGE
z MS SQL Server (ale viz výše oMERGE
problémy)MERGE
od společnosti Oracle (ale viz výše oMERGE
problémy)