PostgreSQL
 sql >> Baza danych >  >> RDS >> PostgreSQL

Jak UPSERT (MERGE, INSERT ... ON DUPLICATE UPDATE) w PostgreSQL?

9,5 i nowsze:

PostgreSQL 9.5 i nowsze wspierają INSERT ... ON CONFLICT (key) DO UPDATE (i ON CONFLICT (key) DO NOTHING ), czyli upsert.

Porównanie z ON DUPLICATE KEY UPDATE .

Szybkie wyjaśnienie.

Aby dowiedzieć się więcej, zapoznaj się z instrukcją, a konkretnie conflict_action klauzulę na diagramie składni i tekst objaśniający.

W przeciwieństwie do rozwiązań dla wersji 9.4 i starszych, które podano poniżej, ta funkcja działa z wieloma sprzecznymi wierszami i nie wymaga wyłącznego blokowania ani pętli ponawiania.

Zatwierdzenie dodawania funkcji jest tutaj, a dyskusja na temat jej rozwoju jest tutaj.

Jeśli korzystasz z wersji 9.5 i nie musisz mieć wstecznej kompatybilności, możesz przestać czytać teraz .

9.4 i starsze:

PostgreSQL nie ma wbudowanego UPSERT (lub MERGE ), a robienie tego skutecznie w obliczu równoczesnego użytkowania jest bardzo trudne.

W tym artykule szczegółowo omówiono problem.

Zasadniczo musisz wybrać jedną z dwóch opcji:

  • Pojedyncze operacje wstawiania/aktualizowania w pętli ponawiania; lub
  • Blokowanie tabeli i wykonywanie scalania wsadowego

Pętla ponawiania poszczególnych wierszy

Używanie upsertów pojedynczych wierszy w pętli ponawiania jest rozsądną opcją, jeśli chcesz, aby wiele połączeń jednocześnie próbowało wykonać wstawianie.

Dokumentacja PostgreSQL zawiera przydatną procedurę, która pozwoli Ci to zrobić w pętli wewnątrz bazy danych. Chroni przed zgubionymi aktualizacjami i wyścigami wstawiania, w przeciwieństwie do większości naiwnych rozwiązań. Będzie działać tylko w READ COMMITTED tryb i jest bezpieczny tylko wtedy, gdy jest to jedyna rzecz, którą robisz w transakcji. Funkcja nie będzie działać poprawnie, jeśli wyzwalacze lub drugorzędne unikalne klucze powodują unikalne naruszenia.

Ta strategia jest bardzo nieefektywna. Gdy tylko jest to praktyczne, powinieneś ustawić się w kolejce i zamiast tego wykonać zbiorcze upsert, jak opisano poniżej.

Wiele prób rozwiązania tego problemu nie uwzględnia wycofywania, więc skutkują niekompletnymi aktualizacjami. Dwie transakcje ścigają się ze sobą; jeden z nich pomyślnie INSERT s; drugi otrzymuje błąd zduplikowanego klucza i wykonuje UPDATE zamiast. UPDATE bloki czekające na INSERT wycofać lub zatwierdzić. Po wycofaniu UPDATE ponowne sprawdzenie warunku pasuje do zera wierszy, więc nawet jeśli UPDATE zatwierdzeń, w rzeczywistości nie zrobił tego, czego się spodziewałeś. Musisz sprawdzić liczbę wierszy wyników i w razie potrzeby spróbować ponownie.

Niektóre próby rozwiązań również nie uwzględniają wyścigów SELECT. Jeśli spróbujesz oczywistego i prostego:

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

wtedy, gdy dwa biegną jednocześnie, istnieje kilka trybów awarii. Jednym z nich jest już omawiany problem z ponownym sprawdzeniem aktualizacji. Innym jest miejsce, w którym zarówno UPDATE w tym samym czasie, dopasowując zero wierszy i kontynuując. Następnie oboje wykonują EXISTS test, który odbywa się przed INSERT . Oba otrzymują zero wierszy, więc oba wykonują INSERT . Jeden kończy się niepowodzeniem z powodu błędu zduplikowanego klucza.

Dlatego potrzebujesz pętli ponownej próby. Możesz pomyśleć, że możesz zapobiec zduplikowanym błędom kluczy lub utraconym aktualizacjom za pomocą sprytnego SQL, ale nie możesz. Musisz sprawdzić liczbę wierszy lub obsłużyć zduplikowane błędy kluczy (w zależności od wybranego podejścia) i spróbować ponownie.

Proszę nie wprowadzać własnego rozwiązania w tym zakresie. Podobnie jak w przypadku kolejkowania wiadomości, prawdopodobnie jest źle.

Wkładka zbiorcza z zamkiem

Czasami chcesz wykonać zbiorcze upsert, w którym masz nowy zestaw danych, który chcesz scalić ze starszym istniejącym zestawem danych. To jest ogromne bardziej wydajne niż pojedyncze rzędy upsert i powinny być preferowane, gdy tylko jest to możliwe.

W takim przypadku zazwyczaj postępuj zgodnie z następującym procesem:

  • CREATE TEMPORARY stół

  • COPY lub zbiorczo wstaw nowe dane do tabeli tymczasowej

  • LOCK tabela docelowa IN EXCLUSIVE MODE . Pozwala to innym transakcjom na SELECT , ale nie wprowadzaj żadnych zmian w tabeli.

  • Wykonaj UPDATE ... FROM istniejących rekordów przy użyciu wartości z tabeli tymczasowej;

  • Wykonaj INSERT wierszy, które jeszcze nie istnieją w tabeli docelowej;

  • COMMIT , zwalniając blokadę.

Na przykład dla przykładu podanego w pytaniu przy użyciu wielowartościowego INSERT aby wypełnić tabelę tymczasową:

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;

Powiązane czytanie

  • UPSERT strona wiki
  • UPSERTyzmy w Postgresie
  • Wstawić, przy zduplikowanej aktualizacji w PostgreSQL?
  • http://petereisentraut.blogspot.com/2010/05/merge-syntax.html
  • Zamęt z transakcją
  • Czy SELECT lub INSERT w funkcji podatnej na wyścigi?
  • SQL MERGE na wiki PostgreSQL
  • Najbardziej idiomatyczny sposób implementacji UPSERT w Postgresql w dzisiejszych czasach

A co z MERGE ?

Standard SQL MERGE w rzeczywistości ma słabo zdefiniowaną semantykę współbieżności i nie nadaje się do wstawiania bez uprzedniego zablokowania tabeli.

Jest to naprawdę przydatne oświadczenie OLAP do scalania danych, ale w rzeczywistości nie jest to przydatne rozwiązanie do wstawiania bezpiecznych współbieżności. Istnieje wiele rad dla osób korzystających z innych DBMS, aby używać MERGE dla upsertów, ale w rzeczywistości jest źle.

Inne bazy danych:

  • INSERT ... ON DUPLICATE KEY UPDATE w MySQL
  • MERGE z MS SQL Server (ale patrz wyżej o MERGE problemy)
  • MERGE z Oracle (ale zobacz powyżej o MERGE problemy)


  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. Gdzie PostgreSQL przechowuje pliki konfiguracyjne/konfiguracyjne?

  2. Jak odbudować niespójne serwery PostgreSQL?

  3. Sprawdzanie, czy tabela postgresql istnieje pod Pythonem (i prawdopodobnie Psycopg2)

  4. Jak zatrzymać/zabić zapytanie w postgresql?

  5. PostgreSQL:sześć nie tak łatwych elementów