You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Laravel中如何基于关联表的用户总分获取排名

获取用户总分排名的实现思路

我有users和achievements两张数据表,每个用户的总分基于achievements表中的数据计算,这部分功能目前运行正常。现在需要根据已计算的总分,获取该用户在所有用户中的排名,比如能得出ID为5的用户排第5名。

现有代码示例

Achievement模型中的定义

public function user()
{
    return $this->belongsTo(User::class);
}

public function getAttributeSum() {
    $sum = 0;
    foreach(func_get_args() as $attribute){
        $sum += $this->getAttribute($attribute);
    }
    return $sum;
}

User模型中的定义

public function achievement()
{
    return $this->hasOne(Achievement::class, 'user_id', 'id');
}

获取用户总分的方式

$achievement = Achievement::where('user_id', $user->id)->first();
$score = $achievement->getAttributeSum('donations', 'shares', 'listens', 'watches', 'readings', 'prayers');

achievements表结构

achievements表结构

待实现的排名功能

现在需要补充获取排名的代码:

$achievement = Achievement::where('user_id', $user->id)->first();
$score = $achievement->getAttributeSum('donations', 'shares', 'listens', 'watches', 'readings', 'prayers');
$placement = ??? // <---- (例如 5 of 2000)

请问有实现思路吗?


实现方案

思路1:数据库层面计算排名(性能更优)

直接通过SQL计算每个用户的总分,统计比当前用户分数高的用户数量,再加1就是排名,适合数据量大的场景:

// 构造总分计算的SQL表达式
$scoreColumns = ['donations', 'shares', 'listens', 'watches', 'readings', 'prayers'];
$scoreExpr = implode(' + ', $scoreColumns);

// 获取当前用户的总分
$currentScore = Achievement::where('user_id', $user->id)->selectRaw($scoreExpr . ' as total')->value('total');

// 统计分数高于当前用户的人数,加1得到排名
$rank = Achievement::selectRaw('COUNT(*) + 1 as rank')
    ->whereRaw("($scoreExpr) > ?", [$currentScore])
    ->value('rank');

// 获取总用户数
$totalUsers = User::count();

// 拼接成目标格式
$placement = "{$rank} of {$totalUsers}";

思路2:模型层面计算(适合小数据量场景)

如果用户数据量不大,可以先拉取所有用户的总分,再通过内存排序计算排名:

// 获取所有用户及对应的总分
$allUsersWithScore = User::with(['achievement'])->get()->map(function($user) {
    return [
        'user_id' => $user->id,
        'total_score' => $user->achievement 
            ? $user->achievement->getAttributeSum('donations', 'shares', 'listens', 'watches', 'readings', 'prayers') 
            : 0
    ];
})->sortByDesc('total_score')->values();

// 查找当前用户的排名(数组索引从0开始,需加1)
$rank = $allUsersWithScore->search(function($item) use ($user) {
    return $item['user_id'] == $user->id;
}) + 1;

$totalUsers = $allUsersWithScore->count();
$placement = "{$rank} of {$totalUsers}";

优化建议

  • 数据量大时优先用思路1,数据库层面计算性能远高于内存排序。
  • 可以考虑新增total_score字段,定时通过队列或数据库触发器更新总分,后续排名查询会更高效;也可以给achievements表的分数字段建立联合索引,提升查询速度。

内容的提问来源于stack exchange,提问作者mafortis

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.26 12:47:35