ВСТАВИТЬ, ЕСЛИ НЕ СУЩЕСТВУЕТ ОБЩЕЕ ОБНОВЛЕНИЕ?

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

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

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

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

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

Ответ 1

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

Ответ 2

Вы должны использовать команду 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'

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

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

Ответ 3

Вам нужно установить ограничение на таблицу, чтобы вызвать " 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.

Ответ 4

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

Ответ 5

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

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

Ответ 6

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... удалит запись перед вставкой новой, если ее придется заменить, что может быть плохо, если у вас есть каскады внешних ключей или другие триггеры удаления.

Ответ 7

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

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

Ответ 8

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

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

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

Ответ 9

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

В качестве примера, если вы хотите вставить запись только в том случае, если в настоящее время не было введено поле NAME, и если это так, вы хотите, чтобы исключение ограничения могло загореться вам, затем ВСТАВИТЬ ИЛИ ЗАМЕНИТЬ не будет выбрасывать и исключать и вместо этого разрешит ограничение UNIQUE, заменив конфликтующую запись (существующая запись тем же NAME). Gaspard's демонстрирует это действительно в его ответе выше.

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