#mysql #sql #bulk
#mysql #sql #массовая
Вопрос:
Есть ли какой-либо способ массового выполнения запроса, подобного INSERT OR UPDATE
, на сервере MySQL?
INSERT IGNORE ...
не сработает, потому что, если поле уже существует, оно просто проигнорирует его и ничего не вставит.
REPLACE ...
не сработает, потому что, если поле уже существует, оно будет сначала DELETE
им, а затем INSERT
им снова, вместо того, чтобы обновлять его.
INSERT ... ON DUPLICATE KEY UPDATE
будет работать, но его нельзя использовать массово.
Итак, я хотел бы знать, есть ли какая-либо команда, подобная INSERT ... ON DUPLICATE KEY UPDATE
, которая может быть выдана массово (более одной строки одновременно).
Комментарии:
1. Почему вы говорите, что ВСТАВКА … При ДУБЛИРОВАНИИ КЛЮЧА ОБНОВЛЕНИЕ не может быть выпущено массово?
2. Вопрос мог бы быть намного яснее. Возможно, то, что искал OP, было массовым обновлением через SQL. Вот каков принятый ответ.
Ответ №1:
Вы можете вставить / обновить несколько строк с помощью INSERT … ПРИ ОБНОВЛЕНИИ ДУБЛИРУЮЩЕГО КЛЮЧА. В документации приведен следующий пример:
INSERT INTO table (a,b,c) VALUES (1,2,3),(4,5,6)
ON DUPLICATE KEY UPDATE c=VALUES(a) VALUES(b);
Или я неправильно понимаю ваш вопрос?
Комментарии:
1. Для тех, кто читает это, более наглядным примером является
INSERT INTO table (a,b,c) VALUES (1,2,3),(4,5,6) ON DUPLICATE KEY UPDATE a=VALUES(a), b=VALUES(b), c=VALUES(c);
то, что таким образом вы можете обновлять столбцы типа updated_at с помощьюVALUES (1,2,NOW())
использования updated_at вместо c, например.2. эй, ребята, если таблица имеет идентификатор auto_increment,
INSERT ... ON DUPLICATE KEY UPDATE
id будет представлять собой непрерывно увеличивающийся серийный номер, как вы это исправили?3. @ZanderWong Я не думаю, что это проблема с MySQL 5.6. Смотрите Этот пример
4. @ZanderWong Это все еще проблема, даже в MySQL 8.x — инструкция «ПРИ ДУБЛИРОВАНИИ КЛЮЧА» увеличивает значение автоматического увеличения в таблицах InnoDB, даже если новая строка не вставлена. Смотрите Руководство по MySQL К сожалению, реального решения этой проблемы нет. Вы должны знать об этом и убедиться, что ваш столбец автоматического увеличения достаточно большой. Вы можете очень быстро записать множество значений, если выполняете массовые обновления, используя инструкцию «ПРИ ДУБЛИРОВАНИИ КЛЮЧА».
5. @ZanderWong установил для innodb_autoinc_lock_mode значение 2 вместо значения по умолчанию 1. Документы MySQL: dev.mysql.com/doc/refman/5.6/en /…
Ответ №2:
Один из возможных способов сделать это — создать временную таблицу, вставить в нее данные, а затем выполнить 1 запрос с объединением, чтобы вставить записи, которые не существуют, за которыми следует обновление до полей, которые существуют. Основы были бы примерно такими.
CREATE TABLE MyTable_Temp LIKE MyTable
LOAD DATA INFILE..... INTO MyTable_Temp
UPDATE MyTable INNER JOIN
MyTable_Temp
ON MyTable.ID=MyTable_Temp.ID
SET MyTable.Col1=MyTable_Temp.Col1, MyTable.Col2=MyTable_Temp.Col2.....
INSERT INTO MyTable(ID,Col1,Col2,...)
SELECT ID,Col1,Col2,...
FROM MyTable_Temp
LEFT JOIN MyTable
ON MyTable_Temp.ID = MyTable.ID
WHERE myTable.ID IS NULL
DROP TABLE MyTable_Temp
Синтаксис может быть неточным, но это должно дать вам основы. Кроме того, я знаю, что это некрасиво, но это выполняет свою работу.
Обновить
Я поменял порядок вставки и обновления, потому что выполнение insert первым приводит к обновлению всех вставленных строк при вызове update. Если вы выполняете обновление первым, обновляются только существующие записи. Это должно означать немного меньше работы для сервера, хотя результаты должны быть одинаковыми.
Комментарии:
1. Что, если два потока попытаются обновить таблицу одновременно, увеличив значение ключа? Может случиться так, что значение ключа не будет обновлено, потому что оно не существовало на этапе ОБНОВЛЕНИЯ и было вставлено другим потоком перед выполнением шага ВСТАВКИ.
2. @Ben Ты прав. Это действительно зависит от варианта использования, в том числе от того, как обновляется таблица, сколько запущено потоков обновления и будут ли эти потоки обновления обновлять одни и те же данные в таблице. Если у вас запущено несколько потоков, обновляющих одни и те же данные, возможно, стоит заблокировать целевую таблицу перед запуском обновления, а затем разблокировать после завершения вставки. Многое зависит от того, чего именно вы пытаетесь достичь, и описанный выше метод работает во многих случаях, когда вы вообще никогда не обновляете таблицу, кроме как из массового CSV-файла.
3. Небольшое улучшение: если вы делаете это внутри транзакции, вы можете изменить ее на
CREATE TEMPORARY TABLE MyTable_Temp like MyTable
, которая будет автоматически уничтожена после фиксации транзакции (и видна только внутри этой транзакции). В противном случае, если вы выполняете несколько массовых обновлений для одной и той же таблицы (даже те, которые по другим причинам не пересекаются), вы можете столкнуться с условиями гонки.4. Будьте осторожны с этим методом, поскольку, если вы находитесь на общем сервере, велика вероятность, что время ожидания запроса истечет, если размер таблиц умеренно велик. Левое соединение не предназначалось для использования подобным образом.
Ответ №3:
Хотя на этот вопрос уже был дан правильный ответ (что MySQL поддерживает это через ON DUPLICATE UPDATE с ожидаемым синтаксисом набора нескольких значений), я хотел бы подробнее остановиться на этом, предоставив демонстрацию, которую может запустить любой пользователь MySQL:
CREATE SCHEMA IF NOT EXISTS `test`;
DROP TABLE IF EXISTS test.new_table;
CREATE TABLE test.new_table (`Key` int(11) NOT NULL AUTO_INCREMENT, PRIMARY KEY (`Key`)) ENGINE=InnoDB AUTO_INCREMENT=106 DEFAULT CHARSET=utf8;
SELECT * FROM test.new_table;
INSERT INTO test.new_table VALUES (1),(2),(3),(4),(5) ON DUPLICATE KEY UPDATE `Key`=`Key` 100;
SELECT * FROM test.new_table;
INSERT INTO test.new_table VALUES (1),(2),(3),(4),(5) ON DUPLICATE KEY UPDATE `Key`=`Key` 100;
SELECT * FROM test.new_table;
Результат выглядит следующим образом:
Empty set (0.00 sec)
Query OK, 5 rows affected (0.00 sec)
Records: 5 Duplicates: 0 Warnings: 0
-----
| Key |
-----
| 1 |
| 2 |
| 3 |
| 4 |
| 5 |
-----
5 rows in set (0.00 sec)
Query OK, 10 rows affected (0.00 sec)
Records: 5 Duplicates: 5 Warnings: 0
-----
| Key |
-----
| 101 |
| 102 |
| 103 |
| 104 |
| 105 |
-----
5 rows in set (0.00 sec)
Ответ №4:
Попробуйте добавить триггер вставки, который выполняет предполетную проверку и отменяет вставку при дублировании ключа (после обновления существующей строки).
Не уверен, что это будет хорошо масштабироваться для массовых вставок, не говоря уже о работе с загрузкой данных в файл, но это лучшее, что я могу придумать. 🙂
Ответ №5:
Если бы вы использовали Oracle или Microsoft SQL, вы могли бы использовать MERGE
. Однако MySQL не имеет прямой корреляции с этим утверждением. Существует упомянутое вами однорядное решение, но, как вы указали, оно не очень хорошо справляется с массовым использованием. Вот сообщение в блоге, которое я нашел о разнице между Oracle и MySQL и о том, как сделать то, что Oracle делает с MERGE
в MySQL:
http://blog.mclaughlinsoftware.com/2009/05/25/mysql-merge-gone-awry/
Это некрасивое решение и, вероятно, оно не такое полное, как вам хотелось бы, но я считаю, что это лучшее из существующих решений.