sql >> Databáze >  >> RDS >> PostgreSQL

Jak UPSERTovat (SPOUČIT, VLOŽIT... PŘI DUPLIKÁTNÍ AKTUALIZACI) v PostgreSQL?

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 a TEMPORARY tabulka

  • COPY nebo hromadně vložte nová data do dočasné tabulky

  • LOCK cílovou tabulku IN EXCLUSIVE MODE . To umožňuje ostatním transakcím SELECT , 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 MySQL
  • MERGE z MS SQL Server (ale viz výše o MERGE problémy)
  • MERGE od společnosti Oracle (ale viz výše o MERGE problémy)


  1. Alternativa k Intersect v MySQL

  2. Aggregační funkce SQLite

  3. Jaký je rozdíl mezi Float a Numeric/Decimal na SQL Server - SQL Server / T-SQL výukový program, část 33

  4. Zkontrolovat, zda soubor existuje nebo ne na serveru SQL?