Страницы

Поиск по вопросам

понедельник, 17 июня 2019 г.

Как отключить проверку уникальности индексов в MySQL?

У меня есть таблица:
CREATE TABLE `payments` ( `id` int(9) unsigned zerofill NOT NULL AUTO_INCREMENT, ... `payments_types_id` smallint(3) unsigned NOT NULL DEFAULT '1', ... `service_id` int(9) NOT NULL DEFAULT '0', PRIMARY KEY (`id`), UNIQUE KEY `index_service_id` (`service_id`,`payments_types_id`), ) ENGINE=InnoDB AUTO_INCREMENT=900199076 DEFAULT CHARSET=cp1251
Проблема касается индекса index_service_id. Я хочу вставить данные в таблицу без заполнения поля service_id, а потом обновить таблицу, сделав значение этого поля равным значению поля id, что обеспечит уникальность индекса index_service_id. На время вставки и обновления таблицы я отключаю проверку уникальности индексов:
/*!40014 SET @OLD_UNIQUE_CHECKS=@@UNIQUE_CHECKS, UNIQUE_CHECKS=0 */;
LOCK TABLES `payments` WRITE;
insert into `payments`(`id`,...) values (100000000, ...), ..., (999999999, ...); //поля service_id в списке параметров нет
update `payments` set `service_id` = `id`;
UNLOCK TABLES;
/*!40014 SET UNIQUE_CHECKS=@OLD_UNIQUE_CHECKS */;
но всё равно получаю ошибку:
Query: insert into payments(id, ... Error Code: 1062 Duplicate entry '0-0' for key 'index_service_id'
В чём причина ошибки? Почему она происходит даже после отключения проверки уникальности индексов? Или как правильно отключить эту проверку?
UPD
Кроме того, очень хотелось бы сделать это без изменения схемы таблицы, если это возможно.


Ответ

Документация про unique_checks
Setting this variable to 0 does not require storage engines to ignore duplicate keys. An engine is still permitted to check for them and issue duplicate-key errors if it detects them.
Эта настройка - рекомендация для storage engine, а не требование.
Mysql штука странная, но, к счастью, проверку уникальных ограничений так просто как проверку внешних ключей отключить не получится. Поэтому используйте нормальные подходы, принятые и для остальных баз данных.
Разрешить NULL, например. Но на большой табличке - не в курсе, как сейчас у mysql, можно ли удалить ограничение not null без пересборки таблицы. удалить уникальный индекс, вставить данные, создать уникальный индекс обратно. Судя по допустимости использования LOCK TABLE WRITE, это не OLTP нагрузка. Затраты времени на пересборку индекса - в зависимости от того, какая часть данных обновляется, а какая остаётся. подготовить необходимые данные в temporary table, затем сделать insert .. select из временной таблицы в реальную.
Последний пункт более интересный, не требующий снимать not null только ради записи и не надо пересобирать индекс. Вариант простой и быстрый, lock table вообще не нужен. Но есть одна большая сложность, связанная с тем, что вы хотите сделать service_id = id: у mysql нет штатной возможности получить заранее список id. Насколько это критично?
Можно выкрутиться вот таким хаком:
LOCK TABLES `payments` WRITE;
-- получаем на приложение следующее значение AI SELECT AUTO_INCREMENT FROM information_schema.tables WHERE table_name = 'table_name' AND table_schema = DATABASE( ) ;
ALTER TABLE tbl AUTO_INCREMENT=(текущее_значение с прошлого шага + объём необходимых добавленных строк);
UNLOCK TABLES;
Т.е. резервируем нужного объёма пул ai, которые можем использовать при записи (при этом поле id надо указывать и считать самостоятельно)
insert into `payments`(`id`,`service_id`,`payments_types_id`) values (:ai, :ai, :type), (:ai+1, :ai+1, :type),...;
Можно в будущем внезапно получить проблемы с изменением параметром сервера, например, auto_increment_increment. И вновь table lock там, где он нафиг не нужен.
Иначе, надо понимать природу значения service_id и payments_types_id. Например, может быть вам допустимо использовать простую последовательность чисел, например, отрицательных на время записи - service_id же не объявлен как unsigned, а auto_increment в вашей таблице будет только положительным.

Комментариев нет:

Отправить комментарий