Q & A-Stil
Nachdem ich stundenlang nachgeforscht und mit dem Problem gekämpft hatte, stellte ich fest, dass es zwei Möglichkeiten gibt, dies zu erreichen, abhängig von der Struktur Ihrer Tabelle und davon, ob Sie Fremdschlüsseleinschränkungen aktiviert haben, um die Integrität aufrechtzuerhalten. Ich möchte dies in einem sauberen Format teilen, um den Menschen, die sich möglicherweise in meiner Situation befinden, Zeit zu sparen.
Option 1: Sie können es sich leisten, die Zeile zu löschen
Mit anderen Worten, Sie haben keinen Fremdschlüssel, oder wenn Sie ihn haben, ist Ihre SQLite-Engine so konfiguriert, dass es keine Integritätsausnahmen gibt. Der Weg ist EINFÜGEN ODER ERSETZEN . Wenn Sie versuchen, einen Player einzufügen / zu aktualisieren, dessen ID bereits vorhanden ist, löscht die SQLite-Engine diese Zeile und fügt die von Ihnen bereitgestellten Daten ein. Nun stellt sich die Frage: Was tun, um die alte ID in Verbindung zu halten?
Angenommen , wir möchten UPSERT mit den Daten user_name = 'steven' und age = 32 erstellen.
Schauen Sie sich diesen Code an:
INSERT INTO players (id, name, age)
VALUES (
coalesce((select id from players where user_name='steven'),
(select max(id) from drawings) + 1),
32)
Der Trick ist in der Verschmelzung. Es gibt die ID des Benutzers 'steven' zurück, falls vorhanden, und andernfalls wird eine neue neue ID zurückgegeben.
Option 2: Sie können es sich nicht leisten, die Zeile zu löschen
Nachdem ich mit der vorherigen Lösung herumgespielt hatte, wurde mir klar, dass dies in meinem Fall dazu führen könnte, dass Daten zerstört werden, da diese ID als Fremdschlüssel für eine andere Tabelle fungiert. Außerdem habe ich die Tabelle mit der Klausel ON DELETE CASCADE erstellt , was bedeuten würde, dass Daten stillschweigend gelöscht würden. Gefährlich.
Also dachte ich zuerst an eine IF-Klausel, aber SQLite hat nur CASE . Und dieser CASE kann nicht verwendet werden (oder zumindest habe ich ihn nicht verwaltet), um eine UPDATE- Abfrage durchzuführen, wenn EXISTS (ID von Spielern auswählen, bei denen user_name = 'steven'), und INSERT, wenn dies nicht der Fall ist . No Go.
Und dann habe ich endlich die brutale Gewalt mit Erfolg eingesetzt. Die Logik besteht darin, für jedes UPSERT , das Sie ausführen möchten, zuerst ein INSERT ODER IGNORE auszuführen , um sicherzustellen, dass eine Zeile mit unserem Benutzer vorhanden ist, und dann ein UPDATE auszuführen Abfrage mit genau denselben Daten , die Sie einzufügen versucht haben.
Gleiche Daten wie zuvor: Benutzername = 'Steven' und Alter = 32.
-- make sure it exists
INSERT OR IGNORE INTO players (user_name, age) VALUES ('steven', 32);
-- make sure it has the right data
UPDATE players SET user_name='steven', age=32 WHERE user_name='steven';
Und das ist alles!
BEARBEITEN
Wie Andy kommentiert hat, kann der Versuch, zuerst einzufügen und dann zu aktualisieren, dazu führen, dass Auslöser häufiger als erwartet ausgelöst werden. Dies ist meiner Meinung nach kein Datensicherheitsproblem, aber es ist wahr, dass das Auslösen unnötiger Ereignisse wenig Sinn macht. Daher wäre eine verbesserte Lösung:
-- Try to update any existing row
UPDATE players SET age=32 WHERE user_name='steven';
-- Make sure it exists
INSERT OR IGNORE INTO players (user_name, age) VALUES ('steven', 32);