Я пытаюсь преобразовать базу данных, чтобы использовать utf8mb4 вместо utf8. Все идет хорошо, кроме одного стола:
CREATE TABLE `search_terms` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`search_term` varchar(128) NOT NULL,
`time_added` timestamp NULL DEFAULT NULL,
`count` int(10) unsigned NOT NULL DEFAULT '0',
PRIMARY KEY (`id`),
UNIQUE KEY `search_term` (`search_term`),
KEY `search_term_count` (`count`)
) ENGINE=InnoDB AUTO_INCREMENT=198981 DEFAULT CHARSET=utf8;
По сути, все, что он делает — это сохраняет запись каждый раз, когда кто-то что-то ищет в форме, чтобы мы могли отслеживать количество запросов, очень просто.
Там есть уникальный индекс search_term
потому что мы хотим иметь только одну строку для каждого поискового запроса и вместо этого увеличивать значение счетчика.
Однако при конвертации в utf8mb4 я получаю повторяющиеся ошибки ввода. Вот команда, которую я выполняю:
ALTER TABLE `search_terms` CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
Глядя в базу данных, я вижу различные примеры, подобные этому:
fm2012
fm2012
fm2012
В его текущем наборе символов utf8 все они рассматриваются как уникальные и существуют в базе данных, при этом никогда не возникало проблем с уникальным индексом search_term
,
Но при конвертации в utf8mb4 они теперь считаются равными и выдают ошибку из-за этого индекса.
Я могу понять, как объединить их достаточно легко, но я обеспокоен тем, что это может быть признаком более серьезной основной проблемы. Я не совсем уверен, как это произошло или каковы могут быть последствия, поэтому мои вопросы немного расплывчаты:
Ваша проблема заключается в изменении сортировки: вы используете general_ci
и вы переходите к unicode_ci
: general_ci
довольно простое сопоставление, которое мало что знает о юникоде, но unicode_ci
делает.
Первое «f» в строке примера — это «Латинская буква F полной ширины» (U + FF46), которая считается равной «Латинской букве F» (U + 0066) unicode_ci
но не general_ci
,
Обычно рекомендуется использовать unicode_ci
именно из-за его Unicode-осведомленности, но вы могли бы преобразовать в utf8mb4_general_ci
чтобы предотвратить эту проблему.
Чтобы предотвратить эту проблему в будущем, вы должны нормализовать Ваш вход перед сохранением в БД. Обычно вы бы использовали NFC, но ваш случай, кажется, требует NFKC. Это должно привести все «эквивалентные» строки к одной и той же форме.
Несмотря на то, что было сказано ранее, речь идет не о general_ci
быть более упрощенным, чем unicode_ci
, Да, это может быть правдой, но проблема в том, что вы должны поддерживать его в соответствии с вашим подтипом.
Например, моя база данных utf8_bin
, Я не могу перейти в utf8mb4_unicode_ci
ни к utf8mb4_general_ci
, Эти команды будут выдавать ошибку при обнаружении дубликата ключа. Однако правильное сопоставление utf8mb4_bin
завершается без проблем.