在 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 而不是在数据库中)进行重整排序不会像在数据库中那样快。一旦您必须在查询中添加 LIMIT
和 OFFSET
子句,这种方法也会失败。我不确定自定义排序规则是否对MAX()
类似的函数执行正确的操作,但在 PHP 中执行 mangled-collation 肯定不会,除非您想拉过整个表格,对其进行排序,然后只抓取一个条目。
所以,作为最后的手段,我会考虑在数据库之外进行排序。
如果您不想构建自己的排序规则,另一种选择是在您的表中构建一个可以正确排序的人工列。您可以在 PHP-land 中使用 normalize()
函数(类似于 Jacob 的函数是合理的起点),并将结果作为名为 sortable_title
的列保存在数据库中;然后ORDER BY sortable_title
就可以了。你会想要一个normalize()
PHP 函数来生成这样的列表(没有标点符号,全部小写,去除重音,...):
所以一个简单的 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 字符排序?最佳解决方案的主要内容,如果未能解决你的问题,请参考以下文章