Обновление дубликатов ключей такое же, как и при вставке

158

Я искал вокруг, но не нашел, если это возможно.

У меня есть этот запрос MySQL:

INSERT INTO table (id,a,b,c,d,e,f,g) VALUES (1,2,3,4,5,6,7,8)

Идентификатор поля имеет «уникальный индекс», поэтому их не может быть два. Теперь, если такой же идентификатор уже присутствует в базе данных, я бы хотел его обновить. Но нужно ли мне снова указывать все эти поля, например:

INSERT INTO table (id,a,b,c,d,e,f,g) VALUES (1,2,3,4,5,6,7,8) 
ON DUPLICATE KEY UPDATE a=2,b=3,c=4,d=5,e=6,f=7,g=8

Или:

INSERT INTO table (id,a,b,c,d,e,f,g) VALUES (1,2,3,4,5,6,7,8) 
ON DUPLICATE KEY UPDATE a=VALUES(a),b=VALUES(b),c=VALUES(c),d=VALUES(d),e=VALUES(e),f=VALUES(f),g=VALUES(g)

Я уже все указал во вставке ...

Дополнительное примечание, я хотел бы использовать обходной путь, чтобы получить идентификатор!

id=LAST_INSERT_ID(id)

Я надеюсь, что кто-нибудь скажет мне, что является наиболее эффективным способом.

Рой
источник
9
AFAIK, метод восстановления каждого столбца, a=VALUES(a), b=VALUES(b), ...это путь, по которому вам нужно идти. Вот как я делаю это для всех моих INSERT ON DUPLICATE KEY UPDATEзаявлений.
Valdogg21
4
Просто чтобы уточнить, все изложенное здесь является правильным, если вы понимаете, что ответ на вопрос таков: необходимо ли переформулировать каждый столбец, который вы хотите обновить? Да, если вы хотите вставить или обновить 8 столбцов в таблице из 25 столбцов, вы должны указать 8 столбцов дважды - один раз в части вставки и один раз в части обновления. (Вы можете пропустить первичный ключ в части обновления, но проще всего предварительно отформатировать строку «a = 1, b = 2, c = 3» и встраивать ее дважды.) Однако вам НЕ нужно пересчитывать оставшиеся 17 столбцов ты не хочешь меняться Если это ОБНОВЛЕНИЕ, они сохранят свои существующие значения.
Timberline
3
Я добавил этот комментарий, потому что вопросы и ответы оставляли меня неуверенным в отношении безымянных столбцов с этим типом INSERT, который я затем протестировал, узнав, что именно нужно тестировать. INSERT ON DUPLICATE KEY UPDATE оставляет неизменными столбцы неизменными при UPDATE, но дает им значения по умолчанию при INSERT. С REPLACE INTO для не упомянутых столбцов всегда используются значения по умолчанию, а не существующие значения.
Timberline
1
Проверьте stackoverflow.com/questions/2472229/… , я думаю, что есть то, что вы ищете
Chococroc

Ответы:

82

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

Например, если ваши колонки aк gуже установлены как 2на 8; не было бы необходимости повторно обновлять его.

В качестве альтернативы вы можете использовать:

INSERT INTO table (id,a,b,c,d,e,f,g)
VALUES (1,2,3,4,5,6,7,8) 
ON DUPLICATE KEY
    UPDATE a=a, b=b, c=c, d=d, e=e, f=f, g=g;

Чтобы получить idот LAST_INSERT_ID; вам нужно указать приложение для бэкенда, которое вы используете для того же.

Для LuaSQL выборка conn:getlastautoid()получает значение.

hjpotter92
источник
6
Это только для примера. В реальном запросе есть различия. Мой вопрос довольно прост: действительно ли мне нужно указать все поля (и значения в моем первом примере), если они такие же, как во вставке? Я просто хочу вставить все или, если есть уникальное значение соответствия: обновить все.
Рой
1
«Еще одна заметка, я бы хотел использовать эту возможность, чтобы получить удостоверение личности!»
Агамемн
1
@ Tresdin, насколько я могу судить, это просто синтаксический сахар. Лично я никогда не видел, чтобы кто-нибудь использовал a = VALUES (a), b = VALUES (b) и т. Д. Может быть, вы можете присвоить столбцу несколько значений? Не уверен, почему бы вам просто не объединить данные заранее.
DuckPuncher
54
Если в таблице tblуже есть строка (1,10), то: INSERT INTO tbl(id, a) VALUES(1,2) ON DUPLICATE KEY UPDATE a=VALUES(a)a = 2 . Хотя INSERT INTO tbl(id, a) VALUES(1,2) ON DUPLICATE KEY UPDATE a=aбудет установлено = 10
Bùi Việt Thành
2
Почему все против? Это не работает. Как отметил @ Bùi Việt Thành, a=aничего не используя обновления. (Запросы в исходном вопросе на самом деле тоже ничего не обновляют, но, похоже, именно это обновление и было задумано ОП.)
Роджер Дуек
41

Существует специфическое расширение MySQL для SQL, которое может быть тем, что вы хотите - REPLACE INTO

Однако он не работает так же, как «ON DUPLICATE UPDATE».

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

  2. Вы не можете ссылаться на значения в старых строках, поэтому вы не можете сделать эквивалент

    INSERT INTO mytable (id, a, b, c) values ( 1, 2, 3, 4) 
    ON DUPLICATE KEY UPDATE
    id=1, a=2, b=3, c=c + 1;

Я хотел бы использовать обходной путь, чтобы получить идентификатор!

Это должно сработать - last_insert_id () должно иметь правильное значение, если ваш первичный ключ имеет автоинкремент.

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

Кто-то еще предложил, прежде чем вы сможете уменьшить объем печати, выполнив:

INSERT INTO `tableName` (`a`,`b`,`c`) VALUES (1,2,3)
ON DUPLICATE KEY UPDATE `a`=VALUES(`a`), `b`=VALUES(`b`), `c`=VALUES(`c`);
Danack
источник
Итак, я не могу придерживаться первичного ключа перед replace intoзапросом?
Рой
Нет - эта строка удаляется и создается новая строка, которая будет иметь новый первичный ключ.
Данак
Разве это не замедлит процесс на больших наборах данных и, как импорт CSV с 100K или более строк?
Мухаммед Омер Аслам
24

Другого пути нет, все приходится указывать дважды. Первый для вставки, второй в случае обновления.

Рой
источник
9
Это неверно и не должно быть принятым ответом. Ответ от @Danack - правильный ответ.
Уэйн Уоркман
2
Вы должны перечитать вопрос @WayneWorkman, это правильный ответ.
Рой
24

Вот решение вашей проблемы:

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

Выполните следующие действия: учитесь на простом решении.

Шаг 1. Создайте схему таблицы, используя этот SQL-запрос:

CREATE TABLE IF NOT EXISTS `user` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `username` varchar(30) NOT NULL,
  `password` varchar(32) NOT NULL,
  `status` tinyint(1) DEFAULT '0',
  PRIMARY KEY (`id`),
  UNIQUE KEY `no_duplicate` (`username`,`password`)
) ENGINE=InnoDB  DEFAULT CHARSET=latin1 AUTO_INCREMENT=1;

Таблица <code> user </ code> с данными

Шаг 2. Создайте индекс из двух столбцов, чтобы избежать дублирования данных, используя следующий SQL-запрос:

ALTER TABLE `user` ADD INDEX no_duplicate (`username`, `password`);

или, создайте индекс из двух столбцов из GUI следующим образом: Создать индекс из GUI Выберите столбцы для создания индекса Индексы таблицы <code> user </ code>

Шаг 3: Обновите, если существует, вставьте, если не используете следующие запросы:

INSERT INTO `user`(`username`, `password`) VALUES ('ersks','Nepal') ON DUPLICATE KEY UPDATE `username`='master',`password`='Nepal';

INSERT INTO `user`(`username`, `password`) VALUES ('master','Nepal') ON DUPLICATE KEY UPDATE `username`='ersks',`password`='Nepal';

Таблица <code> user </ code> после выполнения вышеуказанного запроса


источник
1
Спасибо за это прохождение - заставил меня понять, высоко ценится, спасибо!
Майкл Нильссон
FYI хранение паролей в виде простого текста является ужасной идеей, так как пытается предотвратить дублирование паролей на уровне базы данных.
OrangeDog
10

На всякий случай, если вы можете использовать язык сценариев для подготовки ваших запросов SQL, вы можете повторно использовать пары поле = значение, используя SET вместо (a,b,c) VALUES(a,b,c).

Пример с PHP:

$pairs = "a=$a,b=$b,c=$c";
$query = "INSERT INTO $table SET $pairs ON DUPLICATE KEY UPDATE $pairs";

Пример таблицы:

CREATE TABLE IF NOT EXISTS `tester` (
  `a` int(11) NOT NULL,
  `b` varchar(50) NOT NULL,
  `c` text NOT NULL,
  UNIQUE KEY `a` (`a`)
) ENGINE=MyISAM DEFAULT CHARSET=latin1;
Крис
источник
4
Вы должны избегать своих значений или выполнять подготовленный оператор со своими значениями.
Чиното Вокро
1
@ChinotoVokro - согласен. Это просто представление «как вы могли бы» и даже не использование каких-либо функций запроса в примере.
Крис
1
Это хорошая альтернатива указанию массива ключей и массива значений. Спасибо.
Марк Карпентер-младший
5

Возможно, вы захотите использовать REPLACE INTOсинтаксис, но при дублировании ключа PRIMARY / UNIQUE он удаляет строку и вставляет новый.

Вам не нужно будет повторно указывать все поля. Тем не менее, вы должны учитывать возможное снижение производительности (зависит от вашей таблицы).

Предостережения:

  • Если у вас есть первичный ключ AUTO_INCREMENT, ему будет присвоен новый
  • Индексы, вероятно, должны быть обновлены
Матей
источник
есть нарушение ограничения, если индекс также является внешним ключом для другой таблицы, он запускается удалением части.
user3290180
4

Я знаю, что уже поздно, но я надеюсь, что кому-то поможет этот ответ

INSERT INTO t1 (a,b,c) VALUES (1,2,3),(4,5,6)
ON DUPLICATE KEY UPDATE c=VALUES(a)+VALUES(b);

Вы можете прочитать учебник ниже здесь:

https://mariadb.com/kb/en/library/insert-on-duplicate-key-update/

http://www.mysqltutorial.org/mysql-insert-or-update-on-duplicate-key-update/

Галанг Пилианг
источник
Хороший ответ. Но использование VALUES () для ссылки на новые строки устарело с MySQL 8.0.20 . Новый подход, подробно описанный по ссылке, заключается в использовании псевдонима для строки. В этом примере псевдоним new:INSERT INTO t1 (a,b,c) VALUES (1,2,3),(4,5,6) AS new ON DUPLICATE KEY UPDATE c = new.a+new.b;
user697473
@ user697473 Я получаю сообщение об ошибке: «У вас есть ошибка в синтаксисе SQL; проверьте правильность синтаксиса в руководстве, соответствующем версии вашего сервера MariaDB, рядом с« AS new ON DUPLICATE KEY UPDATE a = new.a '» , и мой запрос работал (кроме случаев, когда встречались дублирующие ключи, очевидно), прежде чем я реализовал это предложение. Любые идеи?
Аарон
@aaron - извини, нет хороших идей. MySQL 8.0.20 был выпущен в конце апреля; возможно MariaDB еще не догнал. Это может объяснить, почему это дает вам ошибку.
user697473
@ user697473 Да, на самом деле я только что попробовал метод a = VALUES (a) в исходном ответе, и он сработал, в любом случае, спасибо.
Аарон
-7

вы можете использовать вставку игнорировать для такого случая, он будет игнорировать, если он получает дубликаты записей INSERT IGNORE ...; - без ключа дубликата

Питу
источник
Я хотел вставить его, когда его нет, иначе обновить. Не игнорируйте это.
Рой
почему вы хотите обновить его, если вы обновляете предыдущее / то же значение, если это новое значение, то с ним все в порядке, если не вставить, игнорируйте работу для него
pitu
2
Скорее всего, потому что значения могут измениться, и он хочет создать запись, если она не существует, но обновить существующую запись, если она это делает (с изменениями), без накладных расходов на обратную передачу в приложение, а затем обратно в базу данных. очередной раз.
mopsyd