SQL oferuje różne sposoby aktualizacji lub wstawiania danych do tabeli:
Kiedy i co używać:
INSERT.UPDATE z warunkami.MERGE lub UPSERT — to oszczędza złożoność i zmniejsza liczbę zapytań SQL.Przykład (PostgreSQL):
INSERT INTO employees(id, name, salary) VALUES (1, 'Ivan', 100000) ON CONFLICT (id) DO UPDATE SET salary = EXCLUDED.salary;
Wpływ na wydajność i integralność:
Co się stanie, jeśli spróbujesz wykonać UPSERT na unikalnym polu, ale w tej samej transakcji jednocześnie wstawić i zaktualizować ten sam wiersz?
Poprawna odpowiedź: Może wystąpić błąd blokady/konfliktu, ponieważ transakcja spróbuje zmienić ten sam wiersz dwukrotnie. Operacja nie zakończy się lub zakończy się błędem deadlock, w zależności od systemu DB.
Przykład:
BEGIN; INSERT INTO users(id, name) VALUES (1, 'Oleg') ON CONFLICT (id) DO UPDATE SET name = EXCLUDED.name; UPDATE users SET name = 'Petr' WHERE id = 1; COMMIT;
Przy niewłaściwej kolejności i poziomie izolacji może wystąpić konflikt zmian.
Historia nr 1
W jednej logice migracji danych używano kombinacji SELECT, a następnie INSERT lub UPDATE, co czasami prowadziło do tego, że inna transakcja ustawia dane wcześniej i występował błąd „duplicate key”. To spowodowało częste awarie nocnego eksportu danych, konieczna była zmiana logiki na UPSERT.
Historia nr 2
W projekcie na MySQL niewłaściwie stosowano ON DUPLICATE KEY UPDATE, który błędnie uwzględniał złożone unikalne klucze. W rezultacie część danych nie była aktualizowana, a występowały duplikaty. Problem udało się zidentyfikować dopiero na środowisku produkcyjnym.
Historia nr 3
W projekcie używano UPDATE zamiast UPSERT do synchronizacji danych użytkowników. Przy awariach połączenia dochodziło do utraty części danych, ponieważ aktualizacja pomijała nowych użytkowników. Po analizie w pełni wdrożono MERGE/UPSERT.