Laravel按客户最新订阅状态分组统计总数报错排查
问题分析与解决
错误根源
触发1055错误的核心原因:
- 你的
clientSubscriptionStatusLatest关联查询中,分组(groupBy('client_id', 'status'))后使用了未聚合的created_at字段排序,MySQL的only_full_group_by模式要求ORDER BY中的非聚合字段必须出现在GROUP BY子句中,否则判定为逻辑不合法。 - 更关键的是,你把「每个客户取最新一条订阅状态」的关联逻辑,和「统计各状态数量」的分组逻辑混在了一起,导致查询语义混乱。
当前代码的逻辑问题
- 关联与主查询冲突:用
Client::with()加载关联的同时,又手动leftJoin了client_subscription_status表,既想查询客户数据,又想统计状态数量,逻辑拧巴,完全偏离「统计各状态总数」的需求。 - 关联查询逻辑错误:
clientSubscriptionStatusLatest原本应该是每个客户对应一条最新状态记录,但你在关联闭包里加了groupBy和COUNT,把它变成了分组统计每个客户的各状态数量,和关联初衷矛盾,同时触发SQL模式限制错误。
正确实现方式
要实现「每个客户仅取最新订阅状态,再统计各状态的总数」,需先筛选出所有客户的最新状态记录,再基于这些记录做分组统计。
方法一:子查询筛选最新记录(兼容MySQL 5.x)
// 第一步:获取每个客户最新的订阅状态记录的时间 $latestStatusTimes = ClientSubscriptionStatus::query() ->select('client_id', DB::raw('MAX(created_at) as latest_created')) ->groupBy('client_id'); // 第二步:关联原表筛选出最新记录,再统计各状态数量 $statusStats = ClientSubscriptionStatus::query() ->joinSub($latestStatusTimes, 'latest_times', function ($join) { $join->on('client_subscription_status.client_id', '=', 'latest_times.client_id') ->on('client_subscription_status.created_at', '=', 'latest_times.latest_created'); }) ->select( 'status', DB::raw('COUNT(*) as total_rows'), DB::raw('MAX(created_at) as created_at') // 取该状态下最新的记录时间 ) ->groupBy('status') ->get();
方法二:窗口函数(MySQL 8.0+,更高效)
$statusStats = ClientSubscriptionStatus::query() ->selectRaw('status, COUNT(*) as total_rows, MAX(created_at) as created_at') ->whereIn('id', function ($query) { // 用窗口函数取每个客户最新一条状态记录的ID $query->select(DB::raw('FIRST_VALUE(id) OVER (PARTITION BY client_id ORDER BY created_at DESC)')) ->from('client_subscription_status'); }) ->groupBy('status') ->get();
两种方法最终都会返回你期望的格式:
[ ['status' => 1, 'total_rows' => 1000, 'created_at' => '2024-01-01 00:00:00'], ['status' => 2, 'total_rows' => 500, 'created_at' => '2024-01-02 00:00:00'] ]
内容的提问来源于stack exchange,提问作者Ryan H
相关产品推荐
相关产品推荐

