上一篇文章在最后提到“在foreach循环中嵌套查询,这是非常耗费资源的”。当时考虑到本站应用场景简单,查询仅为百位数量级,性能损失忽略不计,就图简单没有深入去重写程序。今天得空,改造了下程序,通过操作数组来达到避开在遍历中使用count查询的目的。
public function tagslst($num)
{
debug('begin');
$tagRes=Db::name('tags')->select();
foreach ($tagRes as $key => $value) {
$tagsnum=Db::name('art_tag')->where('tags_id',$value['id'])->count();
$tagsRes[$key]=array('sort'=>$tagsnum,'id'=>$value['id'],'tag_name'=>$value['tag_name']);//构造键名为sort,键值为count计数的新数组
}
foreach ($tagsRes as $k => $v) {
$sort[]=$v['sort'];
}
array_multisort($sort, SORT_DESC, $tagsRes);//按tags数多少重新排序数组
$tagsRes=array_slice($tagsRes,0,$num);//返回指定部分数据
debug('end');
dump(debug('begin','end').'s');
dump(debug('begin','end','m').'kb');die;
return $tagsRes;
}
测试结果:运行耗时0.05s,内存占用112k。如图:
上一篇是正常思维,通过查询tag表中的id在关联表中做count查询查询,最后以count依据截取需要的部分内容返回给控制器。缺陷在上一篇中提到,将第一步结果遍历后,代入count计数,有多少条数据就要查询多少次数据库,这个性能损失非常大。
今天换个思路来实现相同的目的。首先通过查询中间表中的tags_id列,将查询结果通过array_count_values
函数做一个计数操作(关键就在这里,通过使用数组来计数达到避开循环中使用count查询)。后续对这个数组截取需要的部分在tag表中使用in查询,返回最终查询结果即可。代码如下:
public function tagslst($num)
{
debug('begin');
$tagidRes=Db::name('art_tag')->field('tags_id')->select();
foreach ($tagidRes as $key => $value) {
$tagids[]=$value['tags_id'];
}
$tagids=array_count_values($tagids);
arsort($tagids);
$tagids=array_slice($tagids,0,$num,true);
foreach ($tagids as $k => $v) {
$tag_idRes[]=$k;
}
$map['id']=['in',$tag_idRes];
$tagRes=Db::name('tags')->where($map)->select();
foreach ($tagRes as $key => $value) {
foreach ($tag_idRes as $k => $v) {
if($value['id']==$v){
$tagsRes[$k]=$value;
}
}
}
ksort($tagsRes);
debug('end');
dump(debug('begin','end').'s');
dump(debug('begin','end','m').'kb');die;
return $tagsRes;
}
同样,也使用debug函数测试下相应的性能数据。得到结果如下:
和前面的数据进行对比可见,耗时节约70%,内存消耗减少50%以上。性能提升还是非常明显的。性能提升的关键在用PHP数组内置函数去代替了count计数查询,第二是截取需要的部分进行最后的数据查询。