- A+
在开发中,我们会遇到一对多的绑定关系,比如订单:一条订单对应多条商品数据;商品价格:商品价格在不同用户等级或者VIP的价格优惠不同等等。下面以订单为例:
需求: APP订单列表并展示商品信息且需要分页显示数据
常见的做法: 1. 我们根据用户id 获取订单列表(分页数据)后,循环获取每个订单的商品信息,类似以下代码:
public function getOrderList($userId) { $orderList = $this->getSaleOrderListByUserId($userId); //获取订单列表 连接数据库查询即可 foreach ($orderList as $index => $item){ $orderList[$index]['goods_list'] = $this->getSaleOrderGoodsInfoByOrderId($item['order_id']); //根据订单id 获取订单商品 } return $orderList; }
我们来分析以上代码:
在循环订单列表读取商品列表时,采用了循环的方式,这样需要查询的数据库次数为 count($orderList) + 1 次,如果用户订单数量很多的情况下,就会造成接口请求超时等错误。即使是在有分页的限制情况下,依然会频繁的请求数据库。
那么优化的关键就在于如何减少数据库的请求次数。
思路1:
既然已经获取到了订单列表,是否可以通过mysql 的 in 查询来获取订单的商品条数,再通过代码循环和订单ID(订单表的主键,订单商品表的外键)来匹配呢?
public function getOrderList($userId) { $orderList = $this->getSaleOrderListByUserId($userId); //获取订单列表 连接数据库查询即可 $orderGoodsList = $this->getSaleOrderGOodsLists(array_column($orderList, 'order_id')); //根据订单id 获取订单商品 IN查询 foreach ($orderList as $index => $item) { foreach ($orderGoodsList as $indexs => $items){ if($item['order_id'] == $items['order_id']){ $orderList[$index]['goods_list'][] = $items; } } } }
来看这段代码,降低了数据库的连接次数,但是在循环时,进行双层循环,复杂度是非常高的,在数据量大的情况下,容易内存溢出。
思路2:
既然已经获取到了订单列表以及订单商品信息, 是否可以将 订单商品信息的数组格式转换为,索引为 order_id 的多维数组呢? 这样,直接使用isset 即可匹配到订单商品信息。
接下来,我们来看下php的一个数组函数, array_column(), 官方文档内的说明: https://www.php.net/manual/zh/function.array-column.php,常见的使用方法为,获取二维数组的某一列且返回此列的集合(数组),如在上述代码中获取 order_id ,再使用 in 查询。
我们来看 array_column() 的第三个参数: index_key: 用作返回数组的索引/键的列, 且第二个参数为NULL时,会返回整个数组,示例:
$a = array( array( 'id' => 5698, 'first_name' => 'Bill', 'last_name' => 'Gates', ), array( 'id' => 4767, 'first_name' => 'Steve', 'last_name' => 'Jobs', ), array( 'id' => 3809, 'first_name' => 'Mark', 'last_name' => 'Zuckerberg', ) ); var_dump(array_column($a, NULL, 'id'));
输出为:
array(3) { [5698]=> array(3) { ["id"]=> int(5698) ["first_name"]=> string(4) "Bill" ["last_name"]=> string(5) "Gates" } [4767]=> array(3) { ["id"]=> int(4767) ["first_name"]=> string(5) "Steve" ["last_name"]=> string(4) "Jobs" } [3809]=> array(3) { ["id"]=> int(3809) ["first_name"]=> string(4) "Mark" ["last_name"]=> string(10) "Zuckerberg" } }
所以,我们可以使用array_column() 函数来优化思路1 的代码,如下:
public function getOrderList($userId) { $orderList = $this->getSaleOrderListByUserId($userId); //获取订单列表 连接数据库查询即可 $orderGoodsList = $this->getSaleOrderGOodsLists(array_column($orderList, 'order_id')); //根据订单id 获取订单商品 IN查询 $orderGoodsList = array_column($orderGoodsList, NULL, 'order_id'); foreach ($orderList as $index => $item) { if(isset($orderGoodsList[$item['order_id']])){ $orderList[$index]['goods_list'] = $orderGoodsList[$item['order_id']]; }else{ //此订单无商品 $orderList[$index]['goods_list'] = []; } } }
通过以上代码,我们即降低了代码请求数据库的次数,也降低了代码的复杂度,而且代码也十分的简洁。