我相信很多人都知道,处理德语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 上出现,但其中一些答案现在已经相当旧了,我只想知道是否有其他解决方案在此期间出现。我的意思是,我真的无法理解一个简单的排序规则竟然可以完全改变查询结果。排序顺序是可以,但查询结果本身?
对于稍长的帖子,很抱歉,并提前感谢任何形式的建议或评论。