在 mysql 或 php 中使用 utf 字符排序?最佳解决方案

Posted

技术标签:

【中文标题】在 mysql 或 php 中使用 utf 字符排序?最佳解决方案【英文标题】:Sort using utf characters in mysql or php ? best solutions 【发布时间】:2011-07-08 13:12:26 【问题描述】:

使用 mysql,我正在选择我想要排序的西班牙语歌曲列表。以下是查询返回的名称列表:

¡Decirevilla! 阿罕布拉 123 份小菜 非洲 阿罗兹 决定

排序后的列表应如下所示:

123 份小菜 非洲 阿罕布拉 阿罗兹 ¡Decirevilla! 决定

在我阅读了所有研究之后,我得出结论,使用 MySQL 没有合理的方法来实现这一点。我已经尝试过排序规则、字符集等...但是字符 ¡、? 等...无法根据我想要的结果进行排序。甚至 Á 也没有按照我想要的方式排序......

问题1:这是一个合理的结论吗?

我相信实现这一点的唯一方法是将结果传递给 php 中的数组,然后使用自定义函数对数组进行排序...所有这些都使用函数 usort(需要按值排序,我不需要关心维护密钥关联)。类似的东西:

function normalize($a, $b) 
  if ($a == $b) 
     return 0;
  

  return ($a < $b) ? -1 : 1;



$tracks = array();

while ($row = $result->fetch_assoc()) 
    $tracks[] = $row;


usort($tracks, 'normalize');

问题 2:这是实现自定义排序的最佳方式吗?

这是我碰壁的地方:

问题 3:我不知道如何创建 normalize 函数来根据我的需要对名称进行排序。如何忽略某些字符(¡、?、'、!、¿)以及如何将其他字符替换为自然等价字符(Á -> A、É -> E 等。) 我相信通过忽略某些字符并替换其他字符,我可以实现我正在寻找的排序......

问题 4:所有这些都有意义吗?我走对了吗?

提前感谢您的所有建议。 马可

【问题讨论】:

【参考方案1】:

你可以add your own collation 到 MySQL。然后,您可以忽略任何您不关心的字符,根据需要去除重音符号,并通常以您希望的任何一致方式对事物进行排序。

在客户端(即在 PHP 而不是在数据库中)进行重整排序不会像在数据库中那样快。一旦您必须在查询中添加 LIMITOFFSET 子句,这种方法也会失败。我不确定自定义排序规则是否对MAX() 类似的函数执行正确的操作,但在 PHP 中执行 mangled-collat​​ion 肯定不会,除非您想拉过整个表格,对其进行排序,然后只抓取一个条目。

所以,作为最后的手段,我会考虑在数据库之外进行排序。

如果您不想构建自己的排序规则,另一种选择是在您的表中构建一个可以正确排序的人工列。您可以在 PHP-land 中使用 normalize() 函数(类似于 Jacob 的函数是合理的起点),并将结果作为名为 sortable_title 的列保存在数据库中;然后ORDER BY sortable_title 就可以了。你会想要一个normalize() PHP 函数来生成这样的列表(没有标点符号,全部小写,去除重音,...):

123 份小菜 非洲 阿罕布拉 阿罗兹 decirevilla 决定

所以一个简单的 ASCII-betical 排序将做正确的事情。当然,您必须在执行 INSERT 时初始化 sortable_title 并在 UPDATE 期间重新生成它,但如果您的代码被正确封装,这应该是相当简单的。

问题 4:我认为我不同意 Jacob 的观点,并说您将排序规则移出数据库并没有朝着正确的方向前进。我并不是说你完全偏离了轨道,但你最好让 MySQL 处理排序,即使你最终可能会给它一些帮助,比如上面概述的 sortable_title hack。

【讨论】:

如果我在共享主机上,我可以将自己的排序规则添加到 MySQL 吗? @Marco:这取决于托管服务提供商,但我可能倾向于“可能不会”。如果你做不到,那么sortable_title 方法几乎也能完成这项工作。 我刚刚完成了这两种方法的编程,带有 sortable_title 的方法要快得多。我添加了一个计时器和 mysql 解决方案的平均结果:0.009 秒... php 解决方案:0.12 秒。奇怪的是我已经缓存了列表(使用 ob_start().. 方法)并且缓存明显变慢了......我猜,在这种特定情况下,打开缓存文件比执行查询要慢。 ..让您想知道并非总是需要在php中进行缓存... @Marco:很好,你甚至测试了哪个效果更好!数据库倾向于进行大量的批量比较和排序,因此 MySQL 的一部分可能一直经过大量优化,一直到内存和磁盘中的字节布局;一个数量级的性能差异并不让我感到惊讶:数据库擅长批量数据处理,这就是它们的用途。【参考方案2】:

问题 2。 这是实现自定义排序的好方法,那么您唯一需要做的真正工作就是比较功能。

问题 3。 使用iconv 将字符串转换为等效的ASCII 可能是值得的。它可以将 UTF-8 转换为 ASCII 并使用 translit,它将匹配无法直接转换为看起来像的字符。

即Á -> A,É -> E,等等。

转换后,您可以使用 preg_replace 或 str_replace 删除不想排序的字符。

这是您可以使用的比较函数的示例。

function normalize_string($string) 
    $ascii = iconv("utf-8","ascii//TRANSLIT", $string);
    return str_replace(array('!', "'", '?'), '', $ascii);

    // or

    return preg_replace('/[!\'?]/', '', $ascii);

    // or depending on how much you do want to replace... \W => any "non-word" character

    return preg_replace('/\W/', '', $ascii);


function custom_str_cmp($a, $b) 
    return strcmp(normalize_string($a), normalize_string($b));


usort($tracks, 'custom_str_cmp');

问题 4。 是的。

【讨论】:

以上是关于在 mysql 或 php 中使用 utf 字符排序?最佳解决方案的主要内容,如果未能解决你的问题,请参考以下文章

phpMyAdmin 不会在数据库中正确显示或插入 Unicode 字符

如何使用 php 从 mysql 数据库中显示非英文字符?

无法在 MySQL 表中插入 ♥ 字符

PHP Mysql字符集utf8mb4支持Emoji表情

无法使用php显示mysql数据库中的特殊字符

php+mysql 解决emoji问题