Znalazłem kilka „odpowiednich” rozwiązań dla klasycznego „Jak wstawić nowy rekord lub zaktualizować go, jeśli już istnieje”, ale nie mogę zmusić żadnego z nich do pracy w SQLite.
Mam tabelę zdefiniowaną w następujący sposób:
CREATE TABLE Book
ID INTEGER PRIMARY KEY AUTOINCREMENT,
Name VARCHAR(60) UNIQUE,
TypeID INTEGER,
Level INTEGER,
Seen INTEGER
Chcę dodać rekord z unikalną nazwą. Jeśli nazwa już istnieje, chcę zmodyfikować pola.
Czy ktoś może mi powiedzieć, jak to zrobić, proszę?
Odpowiedzi:
Zajrzyj na http://sqlite.org/lang_conflict.html .
Chcesz coś takiego:
Zauważ, że każde pole nie znajdujące się na liście wstawiania zostanie ustawione na NULL, jeśli wiersz już istnieje w tabeli. Oto dlaczego dla tej
ID
kolumny jest podselekcja : W przypadku zamiany instrukcja ustawi wartość NULL, a następnie zostanie przydzielony nowy identyfikator.Tego podejścia można również użyć, jeśli chcesz pozostawić określone wartości pól w spokoju, jeśli wiersz w przypadku zamiany jest ustawiony, ale ustaw pole na NULL w przypadku wstawiania.
Na przykład zakładając, że chcesz odejść w
Seen
spokoju:źródło
Level
nie można zastosować tego podejścia.Należy użyć
INSERT OR IGNORE
polecenia, a następnieUPDATE
polecenia: W poniższym przykładziename
jest kluczem podstawowym:Pierwsze polecenie wstawi rekord. Jeśli rekord istnieje, zignoruje błąd spowodowany konfliktem z istniejącym kluczem podstawowym.
Drugie polecenie zaktualizuje rekord (który teraz zdecydowanie istnieje)
źródło
Musisz ustawić ograniczenie dla tabeli, aby wywołać „ konflikt ”, który następnie rozwiązujesz, wykonując zamianę:
Następnie możesz wydać:
„WYBIERZ * Z danych” da ci:
Zauważ, że data.id to „3”, a nie „1”, ponieważ REPLACE wykonuje DELETE i INSERT, a nie UPDATE. Oznacza to również, że musisz upewnić się, że zdefiniujesz wszystkie niezbędne kolumny, w przeciwnym razie otrzymasz nieoczekiwane wartości NULL.
źródło
Najpierw zaktualizuj. Jeśli zmieniona liczba wierszy ma wartość 0, wstaw ją. Jest to najłatwiejszy i odpowiedni dla wszystkich RDBMS .
źródło
Insert or Replace
jest naprawdę bardziej preferowany.INSERT OR REPLACE
zastąpi pozostałe pola wartością domyślną.Jeśli chcesz zachować drugie pole
lub używając UPSERT (składnia została dodana do SQLite w wersji 3.24.0 (2018-06-04))
W
excluded.
prefiks równa wartościVALUES
.źródło
Upsert jest tym, czego chcesz.
UPSERT
do SQLite dodano składnię w wersji 3.24.0 (2018-06-04).Ostrzegamy, że w tym momencie rzeczywiste słowo „UPSERT” nie jest częścią składni upsert.
Prawidłowa składnia to
INSERT INTO ... ON CONFLICT(...) DO UPDATE SET...
a jeśli robisz,
INSERT INTO SELECT ...
select musi przynajmniejWHERE true
rozwiązać dwuznaczność parsera na temat tokenaON
ze składnią łączenia.Ostrzegamy, że
INSERT OR REPLACE...
usunie rekord przed wstawieniem nowego, jeśli trzeba go wymienić, co może być złe, jeśli masz kaskady kluczy obcych lub inne wyzwalacze usuwania.źródło
UPSERT
składni.Jeśli nie masz klucza podstawowego, możesz wstawić, jeśli nie istnieje, a następnie wykonać aktualizację. Tabela musi zawierać co najmniej jeden wpis przed użyciem tego.
źródło
Wierzę, że chcesz UPSERT .
„WSTAW LUB WYMIEŃ” bez dodatkowej sztuczki w tej odpowiedzi zresetuje wszystkie pola, których nie określisz, na NULL lub inną wartość domyślną. (To zachowanie INSERT OR REPLACE nie różni się od UPDATE; jest dokładnie takie jak INSERT, ponieważ tak naprawdę jest INSERT; jednak jeśli chciałeś UPDATE-jeśli-istnieje, prawdopodobnie chcesz semantyki UPDATE i będzie nieprzyjemnie zaskoczony faktycznym wynikiem.)
Podstępem wynikającym z sugerowanej implementacji UPSERT jest w zasadzie użycie INSERT OR REPLACE, ale określenie wszystkich pól za pomocą wbudowanych klauzul SELECT w celu pobrania bieżącej wartości dla pól, których nie chcesz zmieniać.
źródło
Myślę, że warto zauważyć, że może wystąpić tutaj nieoczekiwane zachowanie, jeśli nie do końca rozumiesz, w jaki sposób KLUCZ PODSTAWOWY i UNIKALNY oddziałują na siebie.
Na przykład, jeśli chcesz wstawić rekord tylko wtedy, gdy pole NAME nie jest aktualnie zajęte, a jeśli tak, chcesz, aby zadziałał wyjątek ograniczenia, wówczas WSTAW LUB WYMIENI nie rzuci i wyjątek, a zamiast tego będzie rozwiązać samo ograniczenie UNIKALNE , zastępując konflikt rekordu (istniejący rekord tą samą NAZWĄ ). Gaspard's pokazuje to bardzo dobrze w swojej powyższej odpowiedzi .
Jeśli chcesz, aby zadziałał wyjątek ograniczenia, musisz użyć instrukcji INSERT i polegać na osobnej komendzie UPDATE, aby zaktualizować rekord, gdy dowiesz się, że nazwa nie została przyjęta.
źródło