德语umlauts和UTF8排序,重新审视

5

我相信很多人都知道,处理德语umlauts和UTF8排序规则可能会带来问题。像a = äo = öu = ü这样的内容不仅会影响结果的排序顺序,还会影响实际结果。以下是一个例子,清楚地展示了如何在尝试区分名词的单数和复数版本(Bademantel - 单数,Bademäntel - 复数)时出现问题。

CREATE TABLE keywords (
    id INT (11) PRIMARY KEY AUTO_INCREMENT,
    keyword VARCHAR (255) NOT NULL
) ENGINE = MyISAM DEFAULT CHARACTER
SET = utf8 COLLATE = utf8_unicode_ci;

INSERT INTO keywords (keyword) VALUES ('Bademantel'), ('Bademäntel');

SELECT * FROM keywords WHERE keyword LIKE ('%Bademäntel%');

结果应该是:
+----+------------+
| id | keyword    |
+----+------------+
|  1 | Bademäntel |
+----+------------+

然而,使用utf8_unicode_ci时,输出结果为:

+----+------------+
| id | keyword    |
+----+------------+
|  1 | Bademantel |
|  2 | Bademäntel |
+----+------------+

这明显不是所需的结果。

实际问题与我的当前项目有关。它涉及编写关键字解析器,基本上应该用网站上每个关键字的链接替换每个出现的关键字到相应的产品页面。为了避免不必要的资源浪费,只获取不同的关键字,但使用以下两种方法之一:

SELECT keyword FROM keywords GROUP BY keyword ORDER BY LENGTH(keyword) DESC

或者
SELECT DISTINCT keyword FROM keywords ORDER BY LENGTH(keyword) DESC

如果不在查询中获取所有非umlaut版本的单词,将导致无法处理(链接)它们,因此所有包含Bademäntel关键字的单词都将被获取,但Bademantel将被省略。

现在我意识到有几种解决这个问题的方法。

1)对关键字表或查询使用utf8_swedish_ci,这将有效地使我免于修改大量现有代码。

SELECT DISTINCT keyword COLLATE utf8_swedish_ci AS keyword FROM keywords ORDER BY LENGTH(keyword) DESC;

很遗憾,我不是那么舍得放弃utf8_unicode_ci,因为它有一个非常好的特性:可以将"Eszett"(即ssß视为相同)进行排序,在处理与德语相关的内容时使用瑞典排序感觉有些不合适。

2)修改现有代码以利用utf8_bin

SELECT DISTINCT keyword COLLATE utf8_bin AS keyword FROM keywords ORDER BY LENGTH(keyword) DESC;

这个功能实现得很好,但它有一个严重的缺陷,即所有比较都是区分大小写的,这意味着如果我决定依赖 utf8_bin 作为解决问题的方案,那么进行不区分大小写的查询就会很困难,比如 LIKE('%Mäntel%'),这肯定会忽略像 Bademäntel 这样的记录。
我知道这个问题每隔一段时间就会在 SO 上出现,但其中一些答案现在已经相当旧了,我只想知道是否有其他解决方案在此期间出现。我的意思是,我真的无法理解一个简单的排序规则竟然可以完全改变查询结果。排序顺序是可以,但查询结果本身?
对于稍长的帖子,很抱歉,并提前感谢任何形式的建议或评论。

请检查以下内容是否有帮助:MySQL字符集/排序规则 - Ravinder Reddy
2个回答

3

对于遇到这个问题的其他人,值得注意的是,自MySQL 5.6版本以来,官方支持 utf8_german2_ci 校对规则,解决了上述所有问题。晚来总比不来好。


1
您可以使用二进制检查,使用关键字 WHERE BINARY keyword = 'Bademantel'。结果将是预期的结果。
请查看此 sqlfiddle,其中显示了此内容:
SELECT * FROM stackoverflow WHERE BINARY keyword = 'Bademantel';

| id |    keyword |
|----|------------|
|  1 | Bademantel |

SELECT * FROM stackoverflow WHERE keyword = 'Bademantel';

| id |    keyword |
|----|------------|
|  1 | Bademantel |
|  2 | Bademäntel |

关于这种行为的更多信息,请参见:使用二进制排序规则会产生什么影响?德语最佳MySQL排序规则是什么?

因此,对于带有德语umlauts或法语重音符号或捷克/波兰语特殊字符的应用程序,您必须决定哪种行为对您的应用程序最好。

大多数情况下,utf8_general_ci就可以了,但有时您必须像处理您的Bademantel一样使用utf8_bin

字符串比较并不差,utf8_general_ci有时会对您有所帮助。如果您已保存类似Straße的字符串-并且您可以搜索Strasse,它也将返回Straße


谢谢您的建议,但我在原始帖子中已经解释了使用显式二进制比较的注意事项,其中最重要的是大小写敏感性。 utf8_german2_ci 是 MySQL 5.6 中引入的明确解决此问题的解决方案。 - user188654
当然可以,除非你被困在安装了MySQL 5.5的生产服务器上。 - CodeBrauer

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接