ВСТАВИТЬ, ЕСЛИ НЕ СУЩЕСТВУЕТ ДРУГОЕ ОБНОВЛЕНИЕ? - PullRequest
252 голосов
/ 03 сентября 2010

Я нашел несколько «подходящих» решений для классического «Как вставить новую запись или обновить ее, если она уже существует», но я не могу заставить ни одну из них работать в SQLite.

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

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

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

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

Ответы [ 9 ]

305 голосов
/ 03 сентября 2010

Посмотрите на 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"));
76 голосов
/ 01 декабря 2013

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

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

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

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

61 голосов
/ 27 января 2011

Вам необходимо установить ограничение для таблицы, чтобы вызвать « конфликт », который затем разрешается путем замены:

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

«ВЫБРАТЬ * ИЗ ДАННЫХ» даст вам:

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

Обратите внимание, что data.id имеет значение "3", а не "1", поскольку REPLACE выполняет DELETE и INSERT, а не UPDATE. Это также означает, что вы должны убедиться, что вы определили все необходимые столбцы, иначе вы получите неожиданные значения NULL.

32 голосов
/ 03 сентября 2010

Сначала обновите его. Если затронуло количество строк = 0, вставьте его. Это самый простой и подходящий для всех СУРБД .

16 голосов
/ 15 мая 2014

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

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

5 голосов
/ 01 января 2016

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

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';
3 голосов
/ 31 октября 2018

Upsert - это то, что вы хотите.Синтаксис UPSERT был добавлен в SQLite с версией 3.24.0 (2018-06-04).

CREATE TABLE phonebook2(
  name TEXT PRIMARY KEY,
  phonenumber TEXT,
  validDate DATE
);

INSERT INTO phonebook2(name,phonenumber,validDate)
  VALUES('Alice','704-555-1212','2018-05-08')
  ON CONFLICT(name) DO UPDATE SET
    phonenumber=excluded.phonenumber,
    validDate=excluded.validDate
  WHERE excluded.validDate>phonebook2.validDate;

Имейте в виду, что на данном этапе фактическое слово "UPSERT" не является частью синтаксиса upsert.

Правильный синтаксис:

INSERT INTO ... ON CONFLICT(...) DO UPDATE SET...

, и если вы делаете INSERT INTO SELECT ..., ваш выбор должен по крайней мере WHERE true, чтобы решить неоднозначность синтаксического анализатора относительно токена ON с синтаксисом соединения.

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

3 голосов
/ 03 августа 2012

Я полагаю, что вы хотите UPSERT .

"INSERT OR REPLACE" без дополнительной хитрости в этом ответе сбрасывает любые поля, которые вы не указали, в NULL или другое значение по умолчанию.(Такое поведение INSERT OR REPLACE отличается от UPDATE; оно в точности похоже на INSERT, потому что на самом деле это INSERT; однако, если вам нужно было UPDATE-if-существует, вы, вероятно, захотите семантику UPDATE и будете неприятно удивлены фактическим результатом.)

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

1 голос
/ 04 мая 2017

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

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

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

...