模拟场景
在我们进行处理业务的过程中,经常会遇到查询两个表的场景。如:要求查询文章表下面所有的评论
文章表article
字段名称
类型
说明
id
int
文章主键
title
varchar
文章标题
评论表comment
|字段名称 | 类型 | 说明|
|--|--|--|
|id|int|评论主键
|article_id|int|文章主键
|content|varchar|评论内容
错误示范
//取出文章的ID
$articleList = M('article')->select();
//遍历文章,进行查看文章下面的评论
forearch($articleList as $k=>$v) {
$map['article_id'] = $v['article_id'];
$commentList = M('comment')->where($map)->select();
$articleList[$k]['commnet_list'] = $commentList;
}
这里就会产生一个问题,对数据库的压力会增大。比如article一共有1000条,那么对应的会进行查询10001次的数据库操作。
正确示范
$articleList = M('article')->select();
$articleMap = [];
foreach($articleList as $k=>$v) {
$articleMap[$v['id']] = $v;
}
$articleIds = array_keys($articleMap);
$articleIds = implode(',', $articleIds);
$commentList = M('comment')->where(array('article_id'=>array('in',$articleIds)));
$result = [];
forearch($commentList as $k=>$v) {
$result[$v['article_id']]['id'] = $articleMap[$v['article_id']]['id'];
$result[$v['article_id']]['title'] = $articleMap[$v['article_id']]['title'];
$result[$v['article_id']]['commentList'][]['id'] = $v['id'];
$result[$v['article_id']]['commentList'][]['title'] = $v['title'];
}
# 最后的结果是
$articleAndCommentList = array_values($result) + $articleList;
这里可以看出,虽然代码增加了,但是对MySQL的压力缺减少,只进行了两次sql查询。
为了解决这一弊端,引入了比较经典的MySQL N+1的问题。
什么是N+1?
A对象关联B对象,A对象进行列表展示时需显示B对象的关联属性,这样需要先用一条sql将N个A对象查询出来,再用N条sql将这些对象的关联属性查询出来。违背了减少数据库交互原则,影响性能。
拓展
在较为成熟的PHP框架中如:ThinkPHP、PhalApi、laravel等,都有对如上的动作进行了封装。
PhalAPI 1.4.2
示例
$aMap = $this->getORMDemo()->fetchPairs('id');
$aIds = array_keys($aMap);
$aIds = implode(',', $aIds);
$sql2 = "select * from comment where article_id in ({$aIds})";
$comments= $this->getORMDemo()->queryAll($sql2,[]);
foreach ($comments as $c){
$aId = $c['article_id'];
echo $c['title'], $aMap[$aId]['title'].'
';
}