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

5
使用MySQL,我正在选择一组西班牙语歌曲,并希望对其进行排序。以下是查询返回的名称列表:
  • ¡Decirevilla!
  • Alhambra
  • 123 pasitos
  • África
  • Arroz
  • Decir
应该按以下方式对已排序列表进行排序:
  • 123 pasitos
  • África
  • Alhambra
  • Arroz
  • ¡Decirevilla!
  • Decir
通过所有我阅读的研究,我得出结论:使用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:我不知道如何创建规范化函数以按照我的需求对名称进行排序。如何忽略某些字符(¡,?,',!,¿),并将其他字符替换为自然等价物(Á-> A,É-> E等)? 我相信通过忽略某些字符和替换其他字符,可以实现我想要的排序...

问题4:这一切都有意义吗?我是否走在了正确的道路上?

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

2个回答

1

你可以在MySQL中添加自己的排序规则。然后,你可以忽略任何你不关心的字符,根据需要去掉重音,并以任何一致的方式对事物进行排序。

在客户端(即在PHP而不是在数据库中)进行混淆排序不会像在数据库中进行那样快速。一旦你必须添加LIMITOFFSET子句到你的查询中,这种方法也会失败。我不确定自定义排序规则是否对MAX()等函数做了正确的处理,但在PHP中进行混淆排序肯定不会,除非你想拉取整个表格,对其进行排序,然后只获取一个条目。

因此,我建议将排序规则放在数据库之外作为最后的选择。

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

  • 123 pasitos
  • africa
  • alhambra
  • arroz
  • decirevilla
  • decir

这样简单的 ASCII 字母排序就能做到正确的排序。当然,在进行 INSERT 时,您需要初始化 sortable_title 并在 UPDATE 期间重新生成它,但如果您的代码被正确封装,这应该是相当简单的。

问题4:我认为我会不同意Jacob的观点,并且说你将排序规则从数据库中移出是不正确的方向。我并不是说你完全偏离了轨道,但是你最好让MySQL处理排序,即使你可能需要使用像上面概述的sortable_titlehack来帮助MySQL。

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

0

问题2。 这是一种实现自定义排序的好方法,那么你唯一需要做的真正工作就是在比较函数上。

问题3。 值得将字符串转换为其ASCII等效项,使用iconv。它可以将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。 是的。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接