MySQL select UTF-8 字符串带有 '=' 但不带有 'LIKE'

MySQL select UTF-8 string with '=' but not with 'LIKE'

我有一个 table,里面有一些来自中世纪书籍的单词,还有一些在现代 latin1 字母表中不再存在的重音字母。我可以很容易地用 UTF-8 组合字符来表示这些字母。例如,要创建带有波浪号的 "J",我使用 UTF-8 序列 \u004A+\u0303 并且 J 变成重音波浪号。

table使用utf8编码,字段排序规则为utf8_unicode_ci。

我的问题如下:如果我尝试 select 整个字符串,我会收到正确的答案。如果我尝试使用 'LIKE' select,我会收到错误的答案。

例如:

mysql> select word, hex(word) from oldword where word = 'hua';
+--------+--------------+
| word   | hex(word)    |
+--------+--------------+
| hũa    | 6875CC8361   |
| huã    | 6875C3A3     |
| hua    | 687561       |
| hũã    | 6875CC83C3A3 |
+--------+--------------+
4 rows in set (0,04 sec)

mysql> select word, hex(word) from oldword where word like 'hua';
+-------+------------+
| word  | hex(word)  |
+-------+------------+
| huã   | 6875C3A3   |
| hua   | 687561     |
+-------+------------+
2 rows in set (0,04 sec)

我不想只搜索整个单词。我想搜索以某个子字符串开头的单词。最终搜索到的词是整个词。

我如何 select 使用 like 的部分字符串并匹配所有字符串?

我尝试使用 this information 创建自定义排序规则,但服务器变得不可用 table 并且只有在经过大量试验和错误之后我才能恢复到 utf8_unicode_ci再次整理,服务器恢复正常。

编辑: 这个网站有问题,有些字符不能正确显示。请查看这些 pastebin 上的结果:

http://pastebin.com/mckJTLFX

http://pastebin.com/WP87QvgB

您可以像通配符一样使用 % 符号。例如:

SELECT word
FROM myTable
WHERE word LIKE 'hua%';

这将拉取所有以 hua 开头且后跟 0+ 个字符的记录。这是一个 SQL Fiddle 示例。

问题是 LIKE 逐个字符地进行比较,当使用 "combining tilda" 时,它实际上是两个字符,尽管它显示为一个(假设您的客户端支持显示它因此)。

永远不会出现比较例如hu~ahua 逐个字符匹配,因为它比较 ~a 的第三个字符。

排序规则(和强制转换)对您有利并在比较整个字符串时处理此类事情,但在逐个字符比较时则不然。

即使您考虑使用 SUBSTRING() 作为 hack 而不是使用 LIKE 和通配符 % 来执行前缀搜索,请考虑以下几点:

SELECT SUBSTRING('hũa', 1, 3) = 'hua'
-> 0
SELECT SUBSTRING('hũa', 1, 4) = 'hua'
-> 1

你必须知道你想要的长度或者像这样强行计算:

SELECT * FROM oldword
WHERE SUBSTRING(word, 1, 3) = 'hua'
   OR SUBSTRING(word, 1, 4) = 'hua'
   OR SUBSTRING(word, 1, 5) = 'hua'
   OR SUBSTRING(word, 1, 6) = 'hua'

看到 Marcus Adams 的回答后,我意识到 REPLACE 函数可能是解决这个问题的方法,尽管他没有提到这个函数。

因为我只有两个不同的组合字符(尖音符和波浪号),与其他 ASCII 字符组合,例如 j 带波浪号,j 带尖音符,m 带波浪号,s 带波浪号,等等。我只需要在使用 LIKE 时替换这两个字符即可。

搜索手册后,我了解了 UNHEX 函数,它帮助我在查询中正确表示单独的组合字符以将其删除。

组合波浪号在十六进制代码中用 CC83 表示,锐音符在十六进制代码中用 CC81 表示。

所以,解决我问题的查询就是这个。

SELECT word, REPLACE(REPLACE(word, UNHEX("CC83"), ""), UNHEX("CC81"), "")
FROM oldword WHERE REPLACE(REPLACE(word, UNHEX("CC83"), ""), UNHEX("CC81"), "") 
LIKE 'hua%';`

根据this

ũ 在 5.6.

上的所有 utf8 归类中归类等于普通 U 在大多数排序中,

排序等同于普通 J;例外:

  • utf8_general*ci因为其实是j加上重音。并且 "general" 归类一次只查看一个 character(与 byte 不同)。大多数排序规则都会考虑多个字符,例如西班牙语中的 chll 或德语中的 ss
  • utf8_roman_ci,真是个怪人。 j́=i=j

(LIKE 不完全遵循常规整理规则。我不熟悉细节,但我认为 J 表示为 2 个字符导致它在 LIKEWHEREORDER BY。此外,我不知道 REPLACE() 是否像 LIKE 或其他地方那样整理。)