【问题标题】:Getting duplicate entry errors when converting utf-8 database to utf8mb4将 utf-8 数据库转换为 utf8mb4 时出现重复输入错误
【发布时间】:2017-03-21 04:02:59
【问题描述】:

我正在尝试将数据库转换为使用 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 时,它们现在被认为是相等的,并且由于该索引而引发错误。

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

  1. 为什么 utf8mb4 将这些与 utf8 区别对待?
  2. 可能的后果是什么?
  3. 有没有什么办法可以进行转换,这样“fm2012”之类的东西就不会出现在我的数据库中,我只有“fm2012”(我也在使用 Laravel 5.1)

【问题讨论】:

  • 老实说,这看起来更像是 UTF-32 和 UTF-16。转换这样做很奇怪。这会发生在普通测试表上吗?
  • 能否粘贴您的错误信息?
  • 转换前的排序规则是什么?这些真的是您的示例字符串“fm2012”中的空格吗?你能把十六进制转储给我们吗?
  • @NishantNair 这是一个简单的“重复条目 'fm2012' 键 'seach_term'”
  • @Beat 它是“utf8_general_ci”,据我所知,它不是空格。它看起来与这个 SO 问题中的完全一样。我不确定你所说的十六进制转储是什么意思

标签: php mysql utf-8 unique-index utf8mb4


【解决方案1】:

您的问题是排序规则的更改:您正在使用 general_ci 并且您正在转换为 unicode_cigeneral_ci 是一个非常简单的排序规则,对 unicode 知之甚少,但 unicode_ci 确实.

示例字符串中的第一个“f”是“全角拉丁小写字母 F”(U+FF46),unicode_ci 认为它等于“拉丁小写字母 F”(U+0066),但 @ 不认为它等于“拉丁小写字母 F”(U+0066) 987654328@.

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

为防止将来出现此问题,您应该先normalize 您的输入,然后再将其保存到数据库中。通常你会使用 NFC,但你的情况似乎需要 NFKC。这应该将所有“等效”字符串带到相同的形式。

【讨论】:

  • 啊,非常感谢,这是有道理的。使用 utf8mb4_general_ci 代替 utf8mb4_unicode_ci 是否还有其他区别?这似乎是一个转换的好机会,但我想这也可能导致更多问题。
  • 有很多不同,见stackoverflow.com/a/2344130/853468
【解决方案2】:

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

例如,我的数据库是utf8_bin。我无法转换为utf8mb4_unicode_ciutf8mb4_general_ci。这些命令将引发发现重复键的错误。但是,正确的排序规则 utf8mb4_bin 可以顺利完成。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2016-01-10
    • 2019-08-29
    • 2019-11-03
    • 1970-01-01
    • 1970-01-01
    • 2012-03-09
    • 2018-04-01
    • 1970-01-01
    相关资源
    最近更新 更多