【问题标题】:normalizing accented characters in MySQL queries规范化 MySQL 查询中的重音字符
【发布时间】:2011-01-19 03:45:54
【问题描述】:

我希望能够进行规范化重音字符的查询,例如:

é, è, and ê

在使用 '=' 和 'like' 的查询中都被视为 'e'。我有一行用户名字段设置为“rené”,我希望能够同时匹配“rene”和“rené” '。

我正在尝试使用 MySQL 5.0.8 中的 'collat​​e' 子句来执行此操作。我收到以下错误:

mysql> select * from User where username = 'rené' collate utf8_general_ci;
ERROR 1253 (42000): COLLATION 'utf8_general_ci' is not valid for CHARACTER SET 'latin1'

FWIW,我的表是通过以下方式创建的:

CREATE TABLE `User` (
  `id` bigint(19) NOT NULL auto_increment,
  `username` varchar(32) NOT NULL,
  PRIMARY KEY  (`id`),
  UNIQUE KEY `uniqueUsername` (`username`)
) ENGINE=InnoDB AUTO_INCREMENT=56790 DEFAULT CHARSET=utf8

【问题讨论】:

  • 如果将表格字符集更改为utf8会怎样?
  • friedo- 这不是 DEFAULT CHARSET=utf8 的作用吗? (我实际上并没有创建此表;我通过“显示创建表用户”对其进行了逆向工程)。
  • 是的,这就是它应该做的。不过,我不知道为什么 MySQL 似乎认为图表集是 latin1,如果你将它设置为 utf8。

标签: sql mysql utf-8 diacritics collate


【解决方案1】:

错误的原因不是表格,而是您输入的字符集,即查询中的“rené”。行为取决于character_set_connection 变量:

用于没有字符集介绍器的文字和数字到字符串转换的字符集。

使用 MySQL 客户端,使用 SET NAMES 更改它:

一个 SET NAMES 'charset_name' 语句等价于这三个语句:

SET character_set_client = charset_name;
SET character_set_results = charset_name;
SET character_set_connection = charset_name;

(来自http://dev.mysql.com/doc/refman/5.5/en/charset-connection.html

示例输出:

mysql> set names latin1;
Query OK, 0 rows affected (0.00 sec)

mysql> select * from User where username = 'rené' collate utf8_general_ci;
ERROR 1253 (42000): COLLATION 'utf8_general_ci' is not valid for CHARACTER SET 'latin1'

mysql> set names utf8;
Query OK, 0 rows affected (0.00 sec)

mysql> select * from User where username = 'rené' collate utf8_general_ci;
Empty set (0.00 sec)

另外,用户可以使用“字符集介绍器”显式设置字符集:

mysql> set names latin1;
Query OK, 0 rows affected (0.00 sec)

mysql> select * from User where username = _utf8'rené' collate utf8_general_ci;
Empty set (0.00 sec)

我知道这个问题已经很老了,但是由于 Google 将我引到这里来解决一个相关问题,我认为它仍然值得回答 :)

【讨论】:

    【解决方案2】:

    我建议您将规范化版本与真实用户名一起保存到您的表中。即时更改编码可能会很昂贵,而且您必须在每次搜索时对每一行再次进行转换。

    如果您使用的是 PHP,则可以使用iconv() 来处理转换:

    $username = 'rené';
    $normalized = iconv('UTF-8', 'ASCII//TRANSLIT', $string);
    

    然后您只需保存两个版本并使用规范化版本进行搜索并使用普通用户名进行显示。如果您还对搜索字符串进行规范化,那么从规范化列进行比较和选择会更快:

    $search = mysql_real_escape_string(iconv('UTF-8', 'ASCII//TRANSLIT', $_GET['search']));
    mysql_query("SELECT * FROM User WHERE normalized LIKE '%".$search."%'");
    

    当然,如果您有多个需要规范化的列,则此方法可能不可行,但在您的特定情况下,这可能没问题。

    【讨论】:

    • 嗯,我对将数据保存在多个地方 (DRY) 有点谨慎,除非它被证明是一个瓶颈。在这种情况下,它将涉及 3 个现有字段 - 用户名、名字和姓氏(为了提出一个简单的问题,我大大简化了我的表结构)。
    • 我使用 PHP 不工作...你需要更多 1 下一行 str_replace("?", "%", $normalized); .. 享受!
    【解决方案3】:

    我已经在 MySQL 中实现了 strtr php function/tr unix 命令,你可以得到源代码here

    你可以用作:

    SELECT tr(name, 'áäèëî', 'aaeei') FROM persons
    

    或者去掉一些字符

    SELECT tr(name, 'áäèëî', null) FROM persons
    

    【讨论】:

      【解决方案4】:
      $normalized = iconv('UTF-8', 'ASCII//TRANSLIT', $string);
      

      是一个完美的php解决方案,但是在mysql中呢?转换?

      在mysql中

      SELECT 'Álvaro José' as accented, (CONVERT ('Álvaro José' USING ascii)) as notaccented
      

      生产:

      Álvaro José     ?lvaro Jos?
      

      重音词不转换为无重音词,不等同于iconv的音译。

      RegExp 不适用于 UTF-8。

      没有任何解决方案。

      【讨论】:

      • 这不是答案,而是包含有用信息的评论
      【解决方案5】:

      使用英文字符的搜索是否返回带有外来字符的结果? 我编写了以下脚本来比较 MySQL 5.7 中的排序规则(也适用于 MariaDB 10.2+):

              $db->query('CREATE TABLE IF NOT EXISTS test (name varchar(20))
               Engine=InnoDB character set utf8mb4 collate utf8mb4_unicode_520_ci');
      
              $db->query('CREATE TABLE IF NOT EXISTS test2 (name varchar(20))
               Engine=InnoDB character set utf8mb4 collate utf8mb4_unicode_ci');
      
              $db->query("insert into test values('Łove 520')");
              $db->query("insert into test2 values('Łove 520')");
      
              $types = ['utf8mb4_unicode_520_ci', 'utf8mb4_unicode_ci'];
              $tables = ['test' => 'utf8mb4_unicode_520_ci', 'test2' => 'utf8mb4_unicode_ci'];
              foreach($types as $n)
              {
                  foreach($tables as $ta => $tc)
                  {
                      $db->query("SET NAMES 'utf8mb4' COLLATE '$n'");
                      $res = $db->query("Select * from $ta where name like 'Love%'"); // Ł equal
                      echo "\ntable $ta($tc), names($n): ".$res->fetchColumn(0);
                  }
              }
      

      结果如下:

      table test(utf8mb4_unicode_520_ci), names(utf8mb4_unicode_520_ci): Łove 520
      table test2(utf8mb4_unicode_ci), names(utf8mb4_unicode_520_ci):
      table test(utf8mb4_unicode_520_ci), names(utf8mb4_unicode_ci): Łove 520
      table test2(utf8mb4_unicode_ci), names(utf8mb4_unicode_ci):
      

      (注意:我从命令行运行脚本,所以它显示为 ┼üove 520 而不是 Łove 520)

      当表排序规则为 utf8mb4_unicode_520_ci 时,无论连接排序规则如何,似乎 L == Ł。但是,如果您只使用 utf8mb4_unicode_ci,则它等效。

      【讨论】:

        猜你喜欢
        • 2014-06-14
        • 1970-01-01
        • 2012-12-25
        • 2015-07-29
        • 2019-02-23
        • 2017-05-09
        • 2012-04-16
        • 2013-02-15
        • 1970-01-01
        相关资源
        最近更新 更多