【问题标题】:SQL doesnt differentiate u and ü although collation is utf8mb4_unicode_ciSQL 不区分 u 和 ü 虽然排序规则是 utf8mb4_unicode_ci
【发布时间】:2023-04-09 12:47:01
【问题描述】:

在表x 中,有一列的值是uü

SELECT * FROM x WHERE column='u'.

这将返回 u AND ü,尽管我只是在寻找 u

表格的排序规则是 utf8mb4_unicode_ci 。无论我在哪里读到类似的问题,每个人都建议使用这种排序规则,因为他们说utf8mb4 确实涵盖了所有字符。使用这种排序规则,应该可以解决所有字符集和排序规则问题。

我可以插入üèéàChinese characters等。当我创建SELECT *时,它们也被正确检索和显示。

仅当我比较两个字符串时,如上例 (SELECT WHERE) 或当我在列上使用 UNIQUE INDEX 时,才会出现此问题。当我使用UNIQUE INDEX 时,当我在列中已有"u" 时,不会插入"ü"。所以,当SQL比较uü以确定ü是否唯一时,它认为它与u相同,并没有插入ü

我将所有内容都更改为utf8mb4,因为我不想再担心字符集和排序规则了。但是,在比较字符串时,utf8mb4 似乎也不是解决方案。

我也试过这个: SELECT * FROM x WHERE _utf8mb4 'ü' COLLATE utf8mb4_unicode_ci = column
这段代码是可执行的(看起来很复杂)。但是,它也返回 ü AND u

我已经与印度和中国的一些人讨论过这个问题。我们还没有找到解决方案。

如果有人能解开这个谜,那就太好了。

Add_On:看完下面的所有答案和cmets,下面是一个解决问题的代码示例:

SELECT * FROM x WHERE 'ü' 整理 utf8mb4_bin = column

通过在 SELECT 查询中添加“COLLATE utf8mb4_bin”,SQL 在查看列中的字符时被邀请戴上“二进制眼镜”(结尾为 _bin)。戴上二进制眼镜后,SQL 现在可以看到列中的二进制代码。并且对于每个可以想到的字母和字符以及表情符号,二进制代码都是不同的。因此,SQL 现在也可以看到 u 和 ü 之间的区别。因此,现在它只在 SELECT 查询查找 ü 时返回 ü,而不返回 u。

通过这种方式,可以保持所有内容(数据库排序规则、表排序规则)相同,但仅在需要精确区分时将“COLLATE utf8mb4_bin”添加到查询中。

(实际上,SQL 摘下了所有其他眼镜(utf8mb4_german_ci、_general_ci、_unicode_ci 等),并且仅在不强制执行任何其他操作时执行它所做的事情。它只是查看二进制代码而不调整其搜索任何特殊的文化背景。)

感谢大家的支持,尤其是 Pred。

【问题讨论】:

  • 从您的描述来看,您似乎确实想要忽略排序规则并执行二进制匹配。你的具体规则是什么?例如,uU 应该被视为相同还是不同?
  • 嗨,阿尔瓦罗。过去我在 stackoverflow 上花了很多时间。但是,我从未发布过问题。反应是压倒性的。 Stackoverflow 似乎是一条通往解决方案的道路。最好的办法是:在比较期间(WHERE 或 UNIQUE),在行中显示不同的所有内容在比较期间也会有所不同。所以,ä 不是 a。 a 不是 A。è 不是 e 等。如果二进制区分所有这些,那可能就是要走的路。我可以在查询期间进行区分而其他一切都保持不变吗?解决这个问题的实用方法是什么?
  • 嗨,阿尔瓦罗。您还询问规则: 1. 它应该是我完全理解的可预测的解决方案。 2. 如果可能,它应该在许多不同的环境中工作。 3. 如果可能的话,应该是一个简单的解决方案。 4. 如果可能的话,它应该很快(现在不是那么重要)。然而,最重要的是:它应该是完全可预测的,并且易于理解和处理。
  • 谢谢,阿尔瓦罗。在阅读了 Pred 下面写的内容后,我也理解了你的回答。是的,这一切的核心是用户对其文化背景的期望。谢谢。

标签: mysql sql utf-8 utf utf8mb4


【解决方案1】:

排序规则和字符集是两个不同的东西。

字符集只是字符及其表示的“无序”列表。 utf8mb4 是一个字符集,涵盖了很多字符。

Collat​​ion 定义字符的顺序(例如确定 order by 的最终结果)并定义其他规则(例如应将哪些字符或字符组合视为相同)。排序规则是从字符集派生的,同一字符集可以有多个排序规则。 (它是字符集的扩展 - sorta)

utf8mb4_unicode_ci 中,所有(大多数?)重音字符都被视为同一个字符,这就是您得到uü 的原因。简而言之,此排序规则是不区分重音的排序规则。

这类似于德国归类将ssß 视为相同的事实。

utf8mb4_bin 是另一种排序规则,它将所有字符视为不同的字符。您可能希望也可能不希望将其用作默认值,这取决于您和您的业务规则。

您也可以在查询中转换排序规则,但请注意,这样做会阻止 MySQL 使用索引。

这是一个使用类似但可能更熟悉的排序规则部分的示例:

排序规则末尾的ci 表示Case Insensitive,几乎所有带有ci 的排序规则都有一对以cs 结尾,表示Case Sensitive

当您的列不区分大小写时,where 条件 column = 'foo' 将找到所有这些:foo Foo fOo FoO FOo FoO fOO, FOO。

现在,如果您尝试将排序规则设置为区分大小写(例如utf8mb4_unicode_cs),上述所有值都会被视为不同的值。

本地化排序规则(如德语、英国、美国、匈牙利等)遵循指定语言的规则。在德国ssß 是相同的,这在德语规则中有说明。当德国用户搜索值 Straße 时,他们会期望软件(支持德语或以德国编写)将返回 StraßeStrasse

更进一步,说到排序,这两个词是相同的,它们是相等的,它们的含义是相同的,所以没有特定的顺序。

不要忘记,UNIQUE 约束只是一种排序/过滤值的方式。因此,如果在使用德语排序规则的列上定义了唯一键,则不允许同时插入 StraßeStrasse,因为根据语言规则,它们应该被视为相等。

现在让我们看看我们的原始排序规则:utf8mb4_unicode_ci,这是一个“通用”排序规则,这意味着它试图简化一切,因为ü 不是一个真正常见的字符,大多数用户不知道如何输入它,这个排序规则使它等于u。这是为了支持大多数语言而进行的简化,但正如您已经知道的那样,这种简化有一些副作用。 (例如排序、过滤、使用唯一约束等)。

utf8mb4_bin 是另一端。此排序规则旨在尽可能严格。为此,它从字面上使用字符代码来区分字符。这意味着,每个字符的形式都是不同的,这种排序规则是隐式区分大小写和重音的。

这两种方法都有缺点:本地化排序规则和通用排序规则是为一种特定语言设计的,或者是为了提供通用解决方案。 (utf8mb4_unicode_ci 是旧 utf8_general_ci 排序规则的“扩展”)

在涉及用户交互时,二进制文件需要格外小心。因为它是CSAS,所以当用户在寻找值'foo' 时,会混淆习惯于获取值'Foo' 的用户。同样作为开发人员,您在连接和其他功能方面必须格外小心。 INNER JOIN 'foo' = 'Foo' 不会返回任何内容,因为 'foo' 不等于 'Foo'。

我希望这些例子和解释能有所帮助。

【讨论】:

  • 你好 Pred。非常感谢,尤其是解释哪个是哪个。我已经在 character_set 和排序规则问题上度过了数周的时间并解决了问题。现在,我想真正了解正在发生的事情并找到一个至少在大多数情况下有效的解决方案,同时确切地知道在哪些情况下它不起作用并了解原因。到目前为止,我不知道排序规则还定义了哪些字符在比较期间被视为相同,尽管排序规则还定义了它们的显示方式不同。这让我的逻辑思维崩溃了。
  • utf8mb4_bin 工作。谢谢。但是,我不明白为什么,也不明白这个解决方案的缺点。因为我不知道所有这些,所以我不知道如何将它集成到来自 php 等的查询中。所以,是时候阅读更多关于 bin 的内容了。你已经帮了我很多了。谢谢。
  • @Jakob 用示例等更新了答案,希望对您有所帮助:)
  • 感谢您的出色回答!您不仅要解释,还要添加示例,以便轻松将您的解释置于正确的上下文中。现在我明白了。所有这些的核心是用户交互。德国用户希望以同样的方式对待 ß 和 ss。并且法国用户可能希望 a 和 à 被视为相同,因为他可能并不总是确定他是否确实添加了重音。如果程序员不使用本地化排序规则,他必须通过调整代码来照顾所有这些用户期望。我现在明白了。最后。哇。我明白了。
  • 欢迎您,祝您编码愉快。我建议首先使用这些示例和不同的排序规则。最重要的是:了解您的业务需求,这将有助于选择正确的排序规则。例如。如果您只为德语或法语用户编写软件,则可以使用本地化排序规则。通用的对于大多数用例来说都很好,它通过将许多字符视为相同来消除许多可能的误解。二进制文件很难使用,它有很多限制,可能需要在应用程序代码中使用更多变通方法。
【解决方案2】:

utf8_collations.html 列出了各种 utf8(或 utf8mb4)排序规则中“相等”的字母。除了极少数例外,在 any ..._ci 排序规则中进行比较之前,所有重音符号都会被去除。一些例外是特定于语言的,而不是一般的 Unicode。示例:冰岛语É > E

..._bin 是唯一将重音字母视为不同的排序规则。折叠外壳也是如此。

如果您要进行大量比较,则应将列的排序规则更改为..._bin。在WHERE 中使用COLLATE 子句时,不能使用索引。

关于ß 的注释。 ss = ß 几乎在所有排序规则中。特别是,utf8_general_ci(曾经是默认值)将它们视为不平等。 one 排序规则没有将任何 2 个字母组合 (ss) 视为单个“字母”。此外,由于 5.0 中的错误,utf8_general_mysql500_ci 将它们视为不平等。

展望未来,utf8mb4_unicode_520_ci 是 5.7 版中最好的。对于 8.0,utf8mb4_0900_ai_ci 是“更好的”。 “520”和“900”指的是Unicode标准,未来可能会有更新的标准。

【讨论】:

    【解决方案3】:

    您可以尝试 utf8_bin 排序规则,您应该不会遇到这个问题,但它会区分大小写。 bin 排序规则比较严格,仅根据选择的编码将字符分开,一旦完成,比较就会在二进制基础上进行,就像许多编程语言会比较字符串一样。

    【讨论】:

    • 嗨,维尔。您写道:“...仅根据所选编码将字符分开,...”。你能给我一个“选择编码”的例子吗?你的意思是 uft8 还是 utf8mb4?
    • 尝试使用 SELECT * FROM x WHERE 'ü' COLLATE utf8_bin = column。让我知道结果如何。
    • 嗨,维尔。现在大笑。这正是我刚才所做的。我对上面的问题进行了补充。是的,它有效。
    • 很高兴知道这一点。继续摇滚吧!
    • @jakob MySQL 的另一个更新:mysqlserverteam.com/…
    【解决方案4】:

    我将在其他答案中添加 _bin 排序规则也有其特殊性。

    例如,在以下之后:

    CREATE TABLE `dummy` (`key` VARCHAR(255) NOT NULL UNIQUE);
    INSERT INTO `dummy` (`key`) VALUES ('one');
    

    这将失败:

    INSERT INTO `dummy` (`key`) VALUES ('one ');
    

    这在The binary Collation Compared to _bin Collations 中有描述。

    编辑:我已经发布了一个相关问题here

    【讨论】:

      猜你喜欢
      • 2015-10-09
      • 2018-12-19
      • 2023-03-22
      • 2015-07-07
      • 2020-11-20
      • 2011-05-11
      • 2014-05-10
      相关资源
      最近更新 更多