【问题标题】:MySQL select UTF-8 string with '=' but not with 'LIKE'MySQL选择带有'='但不带有'LIKE'的UTF-8字符串
【发布时间】:2015-04-12 08:15:03
【问题描述】:

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

表格使用utf8编码,字段排序为utf8_unicode_ci。

我的问题如下:如果我尝试选择整个字符串,我会收到正确的答案。如果我尝试使用“LIKE”进行选择,我会收到错误的答案。

例如:

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)

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

如何使用 like 选择部分字符串并匹配所有字符串?

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

编辑:此站点存在问题,某些字符无法正确显示。请查看这些粘贴箱的结果:

http://pastebin.com/mckJTLFX

http://pastebin.com/WP87QvgB

【问题讨论】:

  • 如果你尝试会发生什么:like 'hua%'
  • 我编辑了这个问题,因为 Stackoverflow 删除了一些重音字符。请看那个pastebin。回答你的问题,它就在这个 pastebin pastebin.com/P6cZVHYE
  • 请在bugs.mysql.com举报
  • @RickJames 你认为这是一个错误吗?起初我以为是,但现在我认为这不是错误,正如我的回答和 MarcusAdams 的回答中所述。
  • 可以通过手册LIKE 部分中的注释解决“错误”,解释如何实现大小写折叠,但组合字符被视为单独的。我们俩;所以我声称它应该采取一些行动。

标签: mysql unicode utf-8 collation


【解决方案1】:

根据this

ũ 在 5.6 上的所有 utf8 排序规则中等于纯 U

在大多数排序规则中等于普通的J;例外:

  • utf8_general*ci 因为它实际上是 j 加上一个口音。并且“一般”排序规则一次只查看一个 character(与 byte 不同)。大多数排序规则会考虑多个字符,例如西班牙语中的 chll 或德语中的 ss
  • utf8_roman_ci,这很奇怪。 j́=i=j

(LIKE 不完全遵循常规的整理规则。我不精通细节,但我认为J 表示为 2 个字符导致它在LIKE 中的工作方式与在@987654336 中的不同@ 或ORDER BY。此外,我不知道REPLACE() 是否像LIKE 或其他地方一样。)

【讨论】:

    【解决方案2】:

    看到 Marcus Adams 的回答后,我意识到 REPLACE 函数可以解决这个问题,虽然他没有提到这个函数。

    因为我只有两个不同的组合字符(acute 和 tilde),与其他 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%';`
    

    【讨论】:

      【解决方案3】:

      问题在于LIKE 逐个字符进行比较,当使用“组合 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'
      

      【讨论】:

      • 我现在想到了另一种可能的解决方案。我只有三个或四个与几个字母组合的组合字符。如果我在使用 LIKE 时使用替换功能删除这些组合字符怎么办?例如:SELECT word from oldword WHERE REPLACE(word, "˜", "") LIKE 'hua%';
      【解决方案4】:

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

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

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

      【讨论】:

      • 问题是“u”和“a”之间有一个组合波浪号。
      • @javsmo 是否有机会全选,或者显示一些没有包含在内的行,以便我可以使用 SQL Fiddle 重新创建它?
      • pastebin 上的第一个选择包含所有匹配 'hua' 的行,带有和不带有重音符号。字符“ũ”使用两个字符,一个“u”和一个组合波浪号。此示例使用的组合也可以用一个字符 \u0169 表示,但还有其他情况,例如带有波浪号的“J”只能用 \u004a+\u0303 表示
      • 这里忽略了重点,问题在于“特殊”字符。不是简单的 ASCII 字符串。
      猜你喜欢
      • 2021-08-13
      • 1970-01-01
      • 1970-01-01
      • 2021-11-27
      • 2016-06-09
      • 2020-07-25
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多