2010-09-03 19 views
207

Ich habe ein paar "wäre" -Lösungen für den Klassiker gefunden "Wie füge ich einen neuen Datensatz ein oder aktualisiere einen, wenn es bereits existiert", aber ich kann keinen von ihnen in SQLite arbeiten.EINFÜGEN, WENN NICHT BESTEHT UPDATE?

Ich habe eine Tabelle wie folgt definiert:

CREATE TABLE Book 
ID  INTEGER PRIMARY KEY AUTOINCREMENT, 
Name VARCHAR(60) UNIQUE, 
TypeID INTEGER, 
Level INTEGER, 
Seen INTEGER 

Was will ich tun ist einen Datensatz mit einem eindeutigen Namen hinzuzufügen. Wenn der Name bereits existiert, möchte ich die Felder ändern.

Kann mir bitte jemand sagen, wie man das macht?

+1

"einfügen oder ersetzen" ist ** völlig anders ** von "einfügen oder aktualisieren" – Fattie

Antwort

267

Werfen Sie einen Blick auf http://sqlite.org/lang_conflict.html.

Sie wollen so etwas wie:

insert or replace into Book (ID, Name, TypeID, Level, Seen) values 
((select ID from Book where Name = "SearchName"), "SearchName", ...); 

Beachten Sie, dass alle Felder nicht in der Einsatzliste wird auf NULL gesetzt werden, wenn die Zeile in der Tabelle bereits vorhanden ist. Aus diesem Grund gibt es einen Subselect für die Spalte ID: Im Ersatzfall würde die Anweisung NULL setzen und dann würde eine neue ID zugewiesen werden.

Dieser Ansatz kann auch verwendet werden, wenn Sie bestimmte Feldwerte allein lassen möchten, wenn die Zeile im Ersetzungsfall das Feld im Einfügefall auf NULL setzt.

Zum Beispiel, vorausgesetzt, Sie Seen allein lassen wollen:

insert or replace into Book (ID, Name, TypeID, Level, Seen) values (
    (select ID from Book where Name = "SearchName"), 
    "SearchName", 
    5, 
    6, 
    (select Seen from Book where Name = "SearchName")); 
+91

Falsches "Einfügen oder Ersetzen" ist anders als "Einfügen oder Aktualisieren". Eine gültige Antwort finden Sie unter http://stackoverflow.com/questions/418898/sqlite-upsert-not-insert-or-replace – rds

+10

@rds Nein, es ist nicht falsch, weil diese Frage sagt "Ändern Sie die Felder" und den Primärschlüssel ist nicht Teil der Spaltenliste, aber alle anderen Felder sind. Wenn Sie Eckfälle haben, in denen Sie nicht alle Feldwerte ersetzen, oder wenn Sie mit dem Primärschlüssel herumspielen, sollten Sie etwas anderes machen. Wenn Sie eine ganze Reihe neuer Felder haben, ist dieser Ansatz in Ordnung. Hast du ein spezifisches Problem, das ich nicht sehen kann? – janm

+8

Es ist gültig, wenn Sie den neuen Wert für alle Felder kennen. Wenn der Benutzer nur das "Level" aktualisiert, kann dieser Ansatz nicht befolgt werden. – rds

29

Zuerst aktualisieren Sie es. Wenn betroffene Zeilenanzahl = 0 ist, fügen Sie sie ein. Es ist die einfachste und geeignet für alle RDBMS.

+8

Nicht sehr Transaktionssicher, es in zwei Operationen zu tun .. – pjc50

+10

Zwei Operationen sollten kein Problem sein mit einer Transaktion auf der richtigen Isolationsstufe, unabhängig von der Datenbank. – janm

+4

'Einfügen oder Ersetzen' ist wirklich mehr vorzuziehen. – MPelletier

55

Sie müssen festlegen eine Einschränkung für die Tabelle einen „conflict“ auszulösen, die Sie dann, indem Sie eine ersetzen lösen:

CREATE TABLE data (id INTEGER PRIMARY KEY, event_id INTEGER, track_id INTEGER, value REAL); 
CREATE UNIQUE INDEX data_idx ON data(event_id, track_id); 

Dann können Sie erteilen:

INSERT OR REPLACE INTO data VALUES (NULL, 1, 2, 3); 
INSERT OR REPLACE INTO data VALUES (NULL, 2, 2, 3); 
INSERT OR REPLACE INTO data VALUES (NULL, 1, 2, 5); 

Die „SELECT * FROM Daten“geben Ihnen:

2|2|2|3.0 
3|1|2|5.0 

Beachten Sie, dass die data.id‚3‘und nicht‚1‘, da ersetzt kein DELETE und INSERT, UPDATE keine. Dies bedeutet auch, dass Sie sicherstellen müssen, dass Sie alle erforderlichen Spalten definieren, oder dass Sie unerwartete NULL-Werte erhalten.

2

Ich glaube, Sie wollen UPSERT.

"INSERT OR REPLACE" ohne die zusätzlichen Tricks in dieser Antwort setzt alle Felder, die Sie nicht angeben, auf NULL oder einen anderen Standardwert zurück. (Dieses Verhalten von INSERT OR REPLACE ist anders als UPDATE; es ist genau wie INSERT, weil es tatsächlich INSERT ist; wenn Sie jedoch UPDATE-wenn-vorhanden wünschen, möchten Sie wahrscheinlich die UPDATE-Semantik und werden vom tatsächlichen Ergebnis unangenehm überrascht.)

Der Trick der vorgeschlagenen UPSERT-Implementierung besteht im Wesentlichen darin, INSERT OR REPLACE zu verwenden, aber alle Felder anzugeben, indem eingebettete SELECT-Klauseln verwendet werden, um den aktuellen Wert für Felder abzurufen, die nicht geändert werden sollen.

52

Sie sollen den INSERT OR IGNORE von einem UPDATE Befehl gefolgt Befehl verwenden: Im folgende Beispiel 'Name' ist ein Primärschlüssel:

Beispiel:

INSERT OR IGNORE INTO my_table (name,age) VALUES('Karen',34) 
UPDATE my_table SET age = 34 WHERE name='Karen' 

Der erste Befehl einfügen der Datensatz. Wenn der Datensatz existiert, ignoriert er den Fehler, der durch den Konflikt mit einem existierenden Primärschlüssel verursacht wurde.

Der zweite Befehl wird der Datensatz aktualisieren (was jetzt existiert, ist auf jeden Fall)

+4

in welchem ​​Moment wird es ignorieren? wenn der Name und das Alter beide gleich sind? – mou

+0

Dies sollte die Lösung sein ... Wenn Sie beim Einfügen einen Trigger verwenden, wird die akzeptierte Antwort jedes Mal ausgelöst. Dies tut nicht und führt nur ein Update – Hariboo

+0

Es ignoriert nur basierend auf dem Namen. Denken Sie daran, dass nur die Spalte "Name" ein Primärschlüssel ist. –

13

INSERT OR REPLACE werden die anderen Felder ersetzen (TypeID, Level) auf den Standardwert.

INSERT OR REPLACE INTO book(id, name) VALUES(1001, 'Programming') 

Ich verwende diese

INSERT OR IGNORE INTO book(id) VALUES(1001); 
UPDATE book SET name = 'Programming' WHERE id = 1001; 

Sie auch

INSERT OR REPLACE INTO book (id, name) 
VALUES (1001, 'Programming', 
    (SELECT typeid FROM book WHERE id = 1001), 
    (SELECT level FROM book WHERE id = 1001), 
) 

aber ich denke, die erste Methode leichter zu lesen

3

Wenn Sie keinen Primärschlüssel verwenden können, Sie können einfügen, wenn nicht vorhanden sind, und dann ein Update durchführen. Die Tabelle muss mindestens einen Eintrag enthalten, bevor Sie diesen verwenden können.

INSERT INTO Test 
    (id, name) 
    SELECT 
     101 as id, 
     'Bob' as name 
    FROM Test 
     WHERE NOT EXISTS(SELECT * FROM Test WHERE id = 101 and name = 'Bob') LIMIT 1; 

Update Test SET id='101' WHERE name='Bob'; 
+0

Dies ist die einzige Lösung, die für mich funktioniert, ohne doppelte Einträge zu erstellen. Wahrscheinlich, weil die Tabelle, die ich verwende, keine Primärschlüssel hat und zwei Spalten ohne Standardwerte hat. Obwohl es sich um eine langwierige Lösung handelt, erfüllt es die Aufgabe richtig und funktioniert wie erwartet. –

1

Ich denke, es lohnt sich unter Hinweis darauf, dass es hier etwas unerwartetes Verhalten sein kann, wenn Sie nicht gründlich verstehen, wie PRIMARY KEY und UNIQUE interact.

Als Beispiel, wenn Sie nur einen Datensatz eingefügt werden sollen, wenn das Name Feld derzeit nicht genommen wird, und wenn es ist, wollen Sie eine Einschränkung Ausnahme Ihnen sagen, zu feuern, dann INSERT OR REPLACE wird nicht ausgelöst und Ausnahme und stattdessen löst die UNIQUE Constraint selbst durch Ersetzen des Konfliktsatzes (der vorhandene Datensatz mit dem gleichen NAME). Gaspard's zeigt dies sehr gut in his answer oben.

Wenn Sie eine Einschränkung Ausnahme feuern wollen, müssen Sie eine INSERT Anweisung verwenden, und stützen sich auf einem separaten UPDATE Befehl, um den Datensatz zu aktualisieren, sobald Sie den Namen wissen nicht genommen wird.

Verwandte Themen