将 utf-8 数据库转换为 utf8mb4 时出现重复输入错误

Getting duplicate entry errors when converting utf-8 database to utf8mb4

我正在尝试将数据库转换为使用 utf8mb4 而不是 utf8。除了一个 table:

之外一切都很好
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 时,它们现在被认为是相等的并且由于该索引而抛出错误。

我可以弄清楚如何轻松地将它们合并在一起,但我担心这可能是更严重的潜在问题的征兆。我不太确定这是怎么发生的,或者后果可能是什么,所以我的问题有点含糊:

  1. 为什么 utf8mb4 对待它们的方式与 utf8 不同?
  2. 可能的后果是什么?
  3. 有没有什么办法可以让我做一个转换,这样像“㽆m2012”之类的东西就不会出现在我的数据库中,我只有“fm2012”(我也在使用Laravel 5.1)

你的问题是排序规则的变化:你正在使用 general_ci 并且你正在转换为 unicode_cigeneral_ci 是一个非常简单的排序规则,了解不多关于 unicode,但 unicode_ci 确实如此。

示例字符串中的第一个“f”是 "Fullwidth Latin Small Letter F" (U+FF46),unicode_ci 认为它等于 "Latin Small Letter F" (U+0066) 但不是general_ci.

通常建议使用 unicode_ci,因为它支持 unicode,但您可以转换为 utf8mb4_general_ci 以防止出现此问题。

为防止将来出现此问题,您应该 normalize 在将输入保存到数据库之前。通常你会使用 NFC,但你的情况似乎需要 NFKC。这应该使所有 "equivalent" 字符串具有相同的形式。

尽管前面说过这并不是 general_ciunicode_ci 更简单。是的,这可能是真的,但问题是您需要使其与您拥有的子类型保持匹配。

比如我的数据库是utf8_bin。我无法转换为 utf8mb4_unicode_ciutf8mb4_general_ci。这些命令将引发发现重复键的错误。但是,正确的整理 utf8mb4_bin 可以毫无问题地完成。