2010-09-03 5 views
207

Я нашел несколько «бы» решений для классического «Как вставить новую запись или обновить ее, если она уже существует», но я не могу заставить их работать в SQLite.INSERT ЕСЛИ НЕ СУЩЕСТВУЕТ ОБЩЕЕ ОБНОВЛЕНИЕ?

У меня есть таблица определяется следующим образом:

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

То, что я хочу сделать, это добавить запись с уникальным именем. Если имя уже существует, я хочу изменить поля.

Может кто-нибудь сказать мне, как это сделать, пожалуйста?

+1

«Вставить или заменить» ** совершенно отличается ** от «вставки или обновления» – Fattie

ответ

267

Посмотрите на 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")); 
+91

Неверная «вставка или замена» отличается от «вставки или обновления». Для правильного ответа см. Http://stackoverflow.com/questions/418898/sqlite-upsert-not-insert-or-replace – rds

+10

@rds Нет, это не так, потому что в этом вопросе говорится «изменить поля» и первичный ключ не входит в список столбцов, но все остальные поля. Если у вас будут угловые случаи, когда вы не заменяете все значения полей, или если вы возитесь с первичным ключом, вы должны делать что-то другое. Если у вас есть полный набор новых полей, этот подход будет прекрасен. У вас есть конкретная проблема, которую я не вижу? – janm

+8

Это действительно, если вы знаете все новое значение для всех полей. Если пользователь обновляет только, скажем, «Уровень», этот подход не может быть соблюден. – rds

29

Во-первых, обновите его. Если повлияло на количество строк строки = 0, тогда вставьте его. Его самый простой и подходящий для всех RDBMS.

+8

Не очень безопасная транзакция для двух операций. – pjc50

+10

Две операции не должны быть проблемой с транзакцией на нужном уровне изоляции, независимо от базы данных. – janm

+4

«Вставить или заменить» действительно предпочтительнее. – MPelletier

55

Вам нужно установить ограничение на стол, чтобы вызвать «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“, потому что ЗАМЕНЫ делает удаления и вставки, а не обновление. Это также означает, что вы должны убедиться, что вы определяете все необходимые столбцы или получите неожиданные значения NULL.

2

Я считаю, что вы хотите UPSERT.

«ВСТАВИТЬ ИЛИ ЗАМЕНИТЬ» без дополнительной обманчивости в этом ответе сбросит все поля, которые вы не указываете в NULL или другое значение по умолчанию. (Такое поведение INSERT OR REPLACE не похоже на UPDATE, это точно так же, как INSERT, потому что на самом деле это INSERT, но если вы хотите UPDATE-if-exist, вы, вероятно, хотите семантику UPDATE и будете неприятно удивлены фактическим результатом.)

Обман из предлагаемой реализации UPSERT в основном заключается в использовании INSERT ИЛИ REPLACE, но укажите все поля, используя встроенные предложения SELECT, чтобы получить текущее значение для полей, которые вы не хотите изменять.

52

Вы должны использовать INSERT OR IGNORE команду, за которым следует UPDATE команды: В следующем примере «имя» является первичным ключом:

пример:

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

Первая команда вставит запись. Если запись существует, она будет игнорировать ошибку, вызванную конфликтом с существующим первичным ключом.

Вторая команда обновит запись (которая в настоящее время, безусловно, существует)

+4

, в этот момент он будет игнорировать? когда имя и возраст одинаковы? – mou

+0

Это должно быть решение ... если вы используете какой-либо триггер на вставке, принятый ответ срабатывает каждый раз. Это не делает и выполняет обновление только – Hariboo

+0

Оно игнорирует, основываясь исключительно на имени. Помните, что только столбец «name» является первичным ключом. –

13

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), 
) 

, но я думаю, что первый способ более легко читать

3

Если у вас нет первичного ключа, Вы можете вставить, если не существует, а затем выполнить обновление. Перед использованием таблица должна содержать хотя бы одну запись.

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

Это единственное решение, которое работало для меня без создания повторяющихся записей. Вероятно, потому, что таблица, которую я использую, не имеет первичных ключей и имеет 2 столбца без значений по умолчанию. Несмотря на то, что это немного длинное решение, оно делает работу должным образом и работает так, как ожидалось. –

1

Я думаю, что стоит отметить, что не может быть какой-то неожиданное поведение здесь, если вы не до конца понять, как PRIMARY KEY и UNIQUE взаимодействуют.

В качестве примера, если вы хотите, чтобы вставить запись только если NAME поле не принято, и если да, то вы хотите ограничение исключения стрелять, чтобы сказать вам, то INSERT OR REPLACE не будет выбрасывать и исключать, а вместо этого будет разрешать ограничение UNIQUE, заменив конфликтующую запись (существующая запись тем же NAME). Gaspard's демонстрирует это действительно хорошо в his answer выше.

Если вы хотите исключение ограничения на огонь, вы должны использовать INSERT заявление, и полагаться на отдельную команду UPDATE обновить запись, как только вы знаете имя не принимается.

Смежные вопросы