获取每支战队前三名的平均成绩,原生SQL在这里,请问怎么用laravel的ORM模型复现呢

1. 运行环境

1). 当前使用的 Laravel 版本?

8.0

2). 当前使用的 php/php-fpm 版本?

PHP 版本:7.4

请教一下各位朋友,这句MySQL如何用ORM复现(主要是我对ORM的临时表使用掌握不熟练):

SELECT
    dt_1.user_clan_id,
    dt_1.title,
    dt_1.clan_avatar,
    dt_1.is_captain,
    AVG(dt_1.speed_max) AS avg_speed_max,
    AVG(dt_1.runball_exponent) AS avg_runball_exponent,
    AVG(dt_1.exponent_molecular) AS avg_exponent_molecular,
    AVG(dt_1.marathon) AS avg_marathon,
    COUNT(
        IF (
            dt_1.user_clan_id = dt_1.user_clan_id,
            TRUE,
            NULL
        )
    ) AS count_t
FROM
    (
        SELECT
            user_clan_members.user_clan_id,
            user_clans.title,
            user_clans.clan_avatar,
            user_clan_members.is_captain,
            user_achievement.speed_max,
            user_achievement.runball_exponent,
            user_achievement.exponent_molecular,
            user_achievement.marathon
        FROM
            user_clans
        LEFT JOIN user_clan_members ON user_clans.id = user_clan_members.user_clan_id
        LEFT JOIN user_achievement ON user_clan_members.user_id = user_achievement.user_id
    ) AS dt_1
WHERE
    (
        SELECT
            COUNT(1)
        FROM
            (
                SELECT
                    user_clan_members.user_clan_id,
                    user_clans.title,
                    user_clans.clan_avatar,
                    user_clan_members.is_captain,
                    user_achievement.speed_max,
                    user_achievement.runball_exponent,
                    user_achievement.exponent_molecular,
                    user_achievement.marathon
                FROM
                    user_clans
                LEFT JOIN user_clan_members ON user_clans.id = user_clan_members.user_clan_id
                LEFT JOIN user_achievement ON user_clan_members.user_id = user_achievement.user_id
            ) AS dt_2
        WHERE
            dt_1.user_clan_id = dt_2.user_clan_id
        AND dt_1.speed_max < dt_2.speed_max
    ) < 3
GROUP BY
    dt_1.user_clan_id
ORDER BY
    dt_1.speed_max DESC

原生查询效果图:
Laravel

这是我最后实现的答案(吐槽还不如我原生SQL),如下:

$param['limit'] = $param['limit'] ?? 15;
//建立临时表
$sql = DB::table('user_clans')
    ->leftJoin('user_clan_members','user_clans.id','=','user_clan_members.user_clan_id')
    ->selectRaw("
        user_clan_members.user_clan_id,
        user_clans.title,
        user_clans.address,
        user_clans.introduction,
        user_clans.telephone,
        user_clans.clan_avatar,
        user_clan_members.avg_speed_max,
        user_clan_members.avg_runball_exponent,
        user_clan_members.avg_exponent_molecular,
        user_clan_members.avg_marathon,
        1000/user_clan_members.avg_marathon AS avg_marathon_asc
    ")
    ->whereRaw("user_clans.status=1 AND user_clan_members.status=2");

//子查询统计各战队成员数量
$sql2 = DB::table('user_clans')
   ->leftJoin('user_clan_members','user_clans.id','=','user_clan_members.user_clan_id')
   ->selectRaw("COUNT(IF (user_clan_members.user_clan_id = user_clan_members.user_clan_id,TRUE,NULL))")
   ->whereRaw("user_clans. STATUS = 1 AND user_clan_members. STATUS = 2 AND dt_1.user_clan_id = user_clan_members.user_clan_id");

//需要展示的字段
$select = "dt_1.user_clan_id,dt_1.title,dt_1.address,dt_1.introduction,dt_1.telephone,
    CONCAT('".StaticDataController::$_server_url . "/',dt_1.clan_avatar) AS clan_avatar,
    ROUND(IF(COUNT(IF(dt_1.user_clan_id=dt_1.user_clan_id,TRUE,NULL))>=3,
    AVG(dt_1.avg_speed_max),0),0) AS avg_speed_max,'rpm' AS avg_speed_max_unit,
    ROUND(IF(COUNT(IF(dt_1.user_clan_id=dt_1.user_clan_id,TRUE,NULL))>=3,
    AVG(dt_1.avg_runball_exponent),0),2) AS avg_runball_exponent,
    ROUND(IF(COUNT(IF(dt_1.user_clan_id=dt_1.user_clan_id,TRUE,NULL))>=3,
    AVG(dt_1.avg_exponent_molecular),0)/1000,3) AS avg_exponent_molecular,
    'km' AS avg_exponent_molecular_unit,
    TRUNCATE(IF(COUNT(IF(dt_1.user_clan_id=dt_1.user_clan_id,TRUE,NULL))>=3,
    AVG(dt_1.avg_marathon),0),0) AS avg_marathon,
    IF(COUNT(IF(dt_1.user_clan_id=dt_1.user_clan_id,TRUE,NULL))>=3,
    1000/AVG(dt_1.avg_marathon),0) AS avg_marathon_asc";

//排序
$sql3 = DB::table(DB::raw("({$sql->toSql()}) as dt_2"))
     ->selectRaw("COUNT(1)")
     ->whereRaw("dt_1.user_clan_id = dt_2.user_clan_id AND dt_1.avg_speed_max < dt_2.avg_speed_max");

//最外层查询出结果
$userClanQuery = DB::table(DB::raw("({$sql->toSql()}) as dt_1"))
     ->selectRaw($select."({$sql2->toSql()}) AS count_t")
     ->whereRaw("({$sql3->toSql()})<3")
     ->groupByRaw("dt_1.user_clan_id")
     ->orderByRaw("avg_speed_max DESC");

 if (!empty($param['title'])){//加入战队名称搜索
      $userClanQuery = $userClanQuery->where('title','like','%'.$param['title'].'%');
 }

$userClan = $userClanQuery->paginate($param['limit']);
《L01 基础入门》
我们将带你从零开发一个项目并部署到线上,本课程教授 Web 开发中专业、实用的技能,如 Git 工作流、Laravel Mix 前端工作流等。
《L04 微信小程序从零到发布》
从小程序个人账户申请开始,带你一步步进行开发一个微信小程序,直到提交微信控制台上线发布。
最佳答案
$sub_query = Model::selectRaw('GROUP_CONCAT(id ORDER BY score desc) as ids, clan_id')
                        ->groupBy('clan_id');

 $query = Model::from('model as c')
                        ->joinSub($sub_query, 'sc', function ($join) {
                            $join->on('sc.clan_id', '=', 'c.clan_id');
                        })
                        ->whereRaw("FIND_IN_SET(id, ids) BETWEEN 1 and 3")
                        ->get();
2年前 评论
IDLIFE (作者) 2年前
aven523 (楼主) 2年前
aven523 (楼主) 2年前
IDLIFE (作者) 2年前
aven523 (楼主) 2年前
讨论数量: 24

实话说 这一坨代码 就写写算了 为啥用ORM 模型实现了

2年前 评论
游离不2

何必把逻辑都寄托在 orm 层?

2年前 评论
游离不2 (作者) 2年前
aven523 (楼主) 2年前
aven523 (楼主) 2年前

写成query builder 我估计更是一脸懵逼

2年前 评论

感觉这玩意 sql就写负责了 不就平均成绩 排名前三的战队吗 能搞这么复杂啊

2年前 评论

事儿真多,键盘给他让他写,写不出来扇他

2年前 评论
fatrbaby

这个就该拆分成多条查询来做

2年前 评论
$sub_query = Model::selectRaw('GROUP_CONCAT(id ORDER BY score desc) as ids, clan_id')
                        ->groupBy('clan_id');

 $query = Model::from('model as c')
                        ->joinSub($sub_query, 'sc', function ($join) {
                            $join->on('sc.clan_id', '=', 'c.clan_id');
                        })
                        ->whereRaw("FIND_IN_SET(id, ids) BETWEEN 1 and 3")
                        ->get();
2年前 评论
IDLIFE (作者) 2年前
aven523 (楼主) 2年前
aven523 (楼主) 2年前
IDLIFE (作者) 2年前
aven523 (楼主) 2年前

感觉还是先优化一下这个sql吧

2年前 评论
arvin-hermit 2年前

:grin:你们是不是电竞 要小心哦

2年前 评论
aven523 (楼主) 2年前

没细看你的SQL,也不清楚实际需求,根据你的标题,可以先把问题拆解下,

然后再一一破解

2年前 评论
orange1994

分步查吧,何必一条查出来,这种语句后面接手的人怎么搞。

2年前 评论
porygonCN

假设战队(team)跟成绩记录应该添加了关联关系 results

# 代码我没跑过 直接在这里撸出来的,差不多是这个思路哈
// 获取战队列表 附带每个战队的前三名成绩
Team::query()->with(["results"=>function($query){
    $query->orderBy("score","desc")->limit(3);
}]);

// 在model中添加 getAvgScoreAttribute() 方法
public function getAvgScoreAttribute(){
  return ceil($this->results->sum()/$this->result->count());
}

缺点是不能以平均分数排序,如果还想根据平均分啥的排序 就需要子查询了

2年前 评论
gbqm 2年前
aven523 (楼主) 2年前

讨论应以学习和精进为目的。请勿发布不友善或者负能量的内容,与人为善,比聪明更重要!