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

197

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

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

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

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

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

  • 2
    «вставить или заменить» совершенно отличается от «вставить или обновить»
Теги:
insert
exists

8 ответов

274
Лучший ответ

Посмотрите 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"));
  • 98
    Неправильное «вставить или заменить» отличается от «вставить или обновить». Для правильного ответа см. Stackoverflow.com/questions/418898/…
  • 11
    @rds Нет, это не так, потому что в этом вопросе написано «изменить поля», а первичный ключ не является частью списка столбцов, но все остальные поля являются. Если у вас будут угловые случаи, когда вы не заменяете все значения полей, или если вы возитесь с первичным ключом, вы должны сделать что-то другое. Если у вас есть полный набор новых полей, этот подход просто подойдет. У вас есть конкретная проблема, которую я не вижу?
Показать ещё 8 комментариев
53

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

47

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

Пример:

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

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

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

  • 6
    в какой момент он будет игнорировать? когда имя и возраст совпадают?
  • 0
    Это должно быть решением ... если вы используете какой-либо триггер на вставке, принятый ответ срабатывает каждый раз. Это не делает и выполняет только обновление
Показать ещё 4 комментария
30

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

  • 8
    Не очень безопасна транзакция, чтобы сделать это за две операции.
  • 11
    У двух операций не должно быть проблем с транзакцией на правильном уровне изоляции, независимо от базы данных.
Показать ещё 7 комментариев
11

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

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

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

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

1

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

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

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

Ещё вопросы

Сообщество Overcoder
Наверх
Меню