SQLite INSERT - NA DUPLICATE KEY UPDATE (UPSERT)

98

MySQL ma coś takiego:

INSERT INTO visits (ip, hits)
VALUES ('127.0.0.1', 1)
ON DUPLICATE KEY UPDATE hits = hits + 1;

O ile wiem, ta funkcja nie istnieje w SQLite, chciałbym wiedzieć, czy istnieje sposób na osiągnięcie tego samego efektu bez konieczności wykonywania dwóch zapytań. Jeśli nie jest to możliwe, co wolisz:

  1. SELECT + (INSERT lub UPDATE) lub
  2. UPDATE (+ INSERT, jeśli UPDATE nie powiedzie się )
Alix Axel
źródło

Odpowiedzi:

31

Od wersji 3.24.0 SQLite obsługuje także upsert , więc teraz możesz po prostu napisać co następuje

INSERT INTO visits (ip, hits)
VALUES ('127.0.0.1', 1)
ON CONFLICT(ip) DO UPDATE SET hits = hits + 1;
szmate1618
źródło
3
Zastanawiałem się, czy możesz zrobić wiele upserttakich w jednej transakcji, tj. Za pomocą executemany()funkcji Pythona ?
Kontrolowane radiowo
117
INSERT OR IGNORE INTO visits VALUES ($ip, 0);
UPDATE visits SET hits = hits + 1 WHERE ip LIKE $ip;

Wymaga to, aby kolumna „ip” miała ograniczenie UNIQUE (lub PRIMARY KEY).


EDYCJA: Kolejne świetne rozwiązanie: https://stackoverflow.com/a/4330694/89771 .

dan04
źródło
2
Tak dla porządku, REPLACEnie ma takiej opcji.
Alix Axel,
1
Jeśli chodzi o link „kolejne świetne rozwiązanie”, rozważałbym również inną odpowiedź na to samo pytanie: stackoverflow.com/a/418988/3650835
KayakinKoder
19

Wolałbym UPDATE (+ INSERT if UPDATE fails). Mniej kodu = mniej błędów.

codeholic
źródło
1
Dzięki! @Sam ( stackoverflow.com/questions/418898/… ) wydaje się zgadzać z tobą. Ja też wolę to podejście.
Alix Axel
@Smith Miałem na myśli używanie zwykłych instrukcji UPDATE i INSERT oraz sprawdzanie wartości zwracanej.
codeholic
To nie ma atomowości, możliwe, że INSERT nie powiedzie się, jeśli jakiś inny proces zostanie wstawiony pomiędzy.
Robin Lavallée
7

Bieżąca odpowiedź będzie działać tylko w sqlite LUB mysql (w zależności od tego, czy użyjesz OR, czy nie). Tak więc, jeśli chcesz kompatybilności z różnymi dbms, wykonaj następujące czynności ...

REPLACE INTO `visits` (ip, value) VALUES ($ip, 0);
Jacob Thomason
źródło
3
Zaakceptowana odpowiedź działa na SQLite (taki był mój cel). REPLACEbędzie działać również na SQLite, ale w MySQL zawsze zresetuje licznik do 0 - podczas gdy zapytanie będzie przenośne, wynik końcowy będzie się znacznie różnić.
Alix Axel,
Masz rację, myślałem, że OP szuka czegoś przenośnego. Zdaję sobie sprawę, że REPLACE INTO nie zadziała we wszystkich przypadkach, szczególnie tam, gdzie konserwacja PK jest potrzebna, ale w wielu przypadkach będzie.
Jacob Thomason,
Czyste niepowodzenie zamiast odrzucania danych jest funkcją, a nie błędem.
Tobu
-4

Powinieneś do tego użyć memcached, ponieważ jest to pojedynczy klucz (adres IP) przechowujący pojedynczą wartość (liczbę odwiedzin). Możesz użyć atomowej funkcji przyrostu, aby upewnić się, że nie ma warunków „wyścigu”.

Jest szybszy niż MySQL i oszczędza obciążenie, więc MySQL może skupić się na innych rzeczach.

Xeoncross
źródło
Jeśli dane nie są takie ważne, tak. Jeśli jednak jest to używane w ruchliwej witrynie, w której wiele adresów IP trafia do usługi, instancja memcached może się zapełnić i spowodować upuszczenie części zawartości. Tworzenie kopii zapasowych zawartości pamięci podręcznej również byłoby interesujące (w razie potrzeby).
Elliot Foster,
@ElliotFoster Memcached może obsłużyć tyle danych, ile wrzucisz do niego pamięć RAM (jeśli chcesz zachować trwałość, użyj także redis lub membase). Jeśli masz ponad 1 milion odwiedzających dziennie, prawdopodobnie możesz pozwolić sobie na przekazanie swojej instancji memcache więcej niż 30 MB pamięci RAM (myślę, że jest to ustawienie domyślne). Jednak z pewnością może obsłużyć znacznie większe obciążenie niż SQLite i MySQL, biorąc pod uwagę ilość pamięci, którą mu dajesz - po prostu nie ma porównania.
Xeoncross
Nie mylcie mojego komentarza jako głosowania przeciwko memcache, ponieważ uważam, że to fantastyczne narzędzie. Podobnie jak redis (nie mogę mówić o membase, ponieważ jej nie używałem). Jednak memcache / redis nie są najbardziej niezawodnymi sklepami. Tak, redis ma trwałość, ale dane są utrwalane na dysku w odstępach czasu (ostatnio sprawdzałem), a memcache wcale. Jak powiedziałem, jeśli dane nie są ważne (lub można je łatwo odtworzyć), to memcache i firma są świetne. W oryginalnym poście pytano również o sqlite, który bardzo różni się od MySQL i prawdopodobnie oznacza, że ​​są one ograniczone w inny sposób.
Elliot Foster
Możesz skonfigurować Redis, aby utrwalał dane tak szybko, jak chcesz (przy X sekund lub przy Y liczbie zmian).
Buffalo,