Я нашел несколько "бы" решений для классических "Как вставить новую запись или обновить ее, если она уже существует", но я не могу заставить их работать в SQLite.
У меня есть таблица, определяемая следующим образом:
CREATE TABLE Book
ID INTEGER PRIMARY KEY AUTOINCREMENT,
Name VARCHAR(60) UNIQUE,
TypeID INTEGER,
Level INTEGER,
Seen INTEGER
Что я хочу сделать, это добавить запись с уникальным именем. Если имя уже существует, я хочу изменить поля.
Может кто-нибудь сказать мне, как это сделать?
Посмотрите http://sqlite.org/lang_conflict.html.
Вам нужно что-то вроде:
insert or replace into Book (ID, Name, TypeID, Level, Seen) values
((select ID from Book where Name = "SearchName"), "SearchName", ...);
Обратите внимание, что любое поле, не содержащее список вставки, будет иметь значение NULL, если строка уже существует в таблице. Вот почему есть подсекция для столбца ID
: в случае замены оператор установит его в NULL, а затем будет выделен свежий идентификатор.
Этот подход также может использоваться, если вы хотите оставить только отдельные значения полей, если строка в случае замены, но установите поле NULL в случае вставки.
Например, если вы хотите оставить только Seen
:
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"));
Вам нужно установить ограничение на таблицу, чтобы вызвать " conflict", который вы затем разрешаете выполнение замены:
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);
Затем вы можете указать:
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);
Данные "SELECT * FROM" предоставят вам:
2|2|2|3.0
3|1|2|5.0
Обратите внимание, что data.id "3", а не "1", потому что REPLACE делает DELETE и INSERT, а не UPDATE. Это также означает, что вы должны убедиться, что вы определяете все необходимые столбцы или получите неожиданные значения NULL.
Вы должны использовать команду INSERT OR IGNORE
, за которой следует команда UPDATE
:
В следующем примере "имя" является первичным ключом:
Пример:
INSERT OR IGNORE INTO my_table (name,age) VALUES('Karen',34)
UPDATE my_table SET age = 34 WHERE name='Karen'
Первая команда вставляет запись. Если запись существует, она будет игнорировать ошибку, вызванную конфликтом с существующим первичным ключом.
Вторая команда обновит запись (которая теперь определенно существует)
Сначала обновите его. Если влияет на количество строк= 0, то вставьте его. Это самый простой и удобный для всех РСУБД.
INSERT OR REPLACE
заменит остальные поля (TypeID
, Level
) на значение по умолчанию.
INSERT OR REPLACE INTO book(id, name) VALUES(1001, 'Programming')
Я использую этот
INSERT OR IGNORE INTO book(id) VALUES(1001);
UPDATE book SET name = 'Programming' WHERE id = 1001;
Вы также можете использовать
INSERT OR REPLACE INTO book (id, name)
VALUES (1001, 'Programming',
(SELECT typeid FROM book WHERE id = 1001),
(SELECT level FROM book WHERE id = 1001),
)
но я думаю, что первый метод легче читать
Если у вас нет первичного ключа, вы можете вставить, если не существует, а затем выполнить обновление. Перед использованием этой таблицы должна быть хотя бы одна запись.
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';
Я считаю, что вы хотите UPSERT.
"ВСТАВИТЬ ИЛИ ЗАМЕНИТЬ" без дополнительного обмана в этом ответе будет reset любые поля, которые вы не указываете в NULL или другое значение по умолчанию. (Это поведение INSERT или REPLACE не похоже на UPDATE, это точно так же, как INSERT, потому что на самом деле это INSERT, но если вы хотите, то UPDATE-if-exists вы, вероятно, хотите семантику UPDATE и будете неприятно удивлены фактическим результатом.)
Обман из предлагаемой реализации UPSERT в основном заключается в использовании INSERT ИЛИ REPLACE, но укажите все поля, используя встроенные предложения SELECT, чтобы получить текущее значение для полей, которые вы не хотите изменять.
Я считаю, что стоит отметить, что здесь может быть какое-то неожиданное поведение, если вы не понимаете, как взаимодействуют PRIMARY KEY и UNIQUE.
В качестве примера, если вы хотите вставить запись только в том случае, если в настоящее время не было введено поле NAME, и если это так, вы хотите, чтобы исключение ограничения могло загореться вам, затем ВСТАВИТЬ ИЛИ ЗАМЕНИТЬ не будет выбрасывать и исключать и вместо этого разрешит ограничение UNIQUE, заменив конфликтующую запись (существующая запись тем же NAME). Gaspard's демонстрирует это действительно в его ответе выше.
Если вы хотите, чтобы исключение ограничения включалось, вы должны использовать инструкцию INSERT и полагаться на отдельную команду UPDATE, чтобы обновить запись, как только вы знаете имя не принимается.