PHP优化循环嵌套问题之array_column和collect集合分组
- 场景:统计推广渠道引入用户数量和带来收益[推广渠道数量少,推广用户数量多]
- 需求:统计推广人、访客数、购买人数、营业额、转化率
- 推广日志表:channel_log (推广人/userId)
- 订单表:order(userId/支付金额)
思路1
a.通过推广日志表,统计推广人带来用户
select 推广用户, GROUP_CONCAT(distinct userId) AS 全部用户Id,count(distinct user_id) AS 用户数 from channel_log group by 推广用户
[
[
'推广人' => 'a',
'全部用户Id' => '1,2,3',
'用户数' => 3
],
....
]
b.通过订单表,whereIn统计推广人带来收益统计
for(推广列表) {
通过 where In 用户Id 统计营业额和购买人数
}
不足:
1、GROUP_CONCAT有长度限制,默认1024个字节,过长会被截取
2、频繁请求数据库,影响效率
优化:减少频繁请求数据库
思路2
a.通过推广日志表获得基础数据
select 推广人, userId from channel_log group by 推广用户,userId
[
[
'推广人' => 'a',
'用户Id' => '1',
],
[
'推广人' => 'b',
'用户Id' => '2',
],
....
]
b.通过订单表,统计全部用户分别消费金额
select 用户ID,消费金额 whereIn 所有用户ID group by 用户ID
c.通过遍历,计算推广人带来收益统计
不足:
1、多层遍历,复杂度比较高,数量过大容易内存溢出
2、whereIn的数量不能过多
优化:减少复杂度
思路3:
1、通过推广日志表获得基础数据(思路2第一步)
2、获取$list后,集合分组
<!-- 集合分组并且排序(对象) -->
$list = collect($list)->groupBy("key")->sortBy(function ($item) {
return count($item);
});
[
{
key: "a",
userId: "1"
},
{
key: "b",
userId: "2"
}
]
转成
{
a: [
{
key: "a",
userId: "1"
}
],
b: [
{
key: "b",
userId: "2"
}
]
}
最后可以转成
[
[
'key' => a,
'userIds' => [1]
],
[
'key' => b,
'userIds' => [2]
]
]
3、通过订单表,统计全部用户分别消费金额(不要whereIn,会造成多余无关用户,后面有剔除)
4、订单统计分组,统计用户消费金额,以用户ID为KEY(为了后续查询)
<!-- 分组(数组) -->
$orderList = array_column($orderList, null, 'userId'); // key值为userId
[
['userId' => 2, 'amount' => 3],
['userId' => 3, 'amount' => 3]
]
转成
[
'2' => [
['userId' => 2, 'amount' => 3],
],
'3' => [
['userId' => 3, 'amount' => 3]
]
]
5、所有用户/支付用户
//所有用户
$allUserIds = array_unique(array_flatten(array_pluck($list, 'userIds')));
// 支付用户:剔除非来源用户,全部为支付订单
$orderUserIds = array_intersect($allUserIds, $orderUserIds);
6、统计数据
foreach(推广列表 as $item) {
$userIds = $item['userIds']// 邀请用户列表
$payUserIds = array_intersect($userIds, $orderUserIds);// 支付用户,减少数据
for($payUserIds as $userId) {
$orderList[$userId] // 复杂度为1
}
}
- 总结:
- 1、尽量减少复杂度,O(1)替代O(N^2)
- 2、减少数据量,用array_intersect
- 3、用户集合分组(collect)和array_column将数组数量减少,降低时间复杂度