
本文详解如何使用 laravel eloquent 结合 join 与 group by,对关联模型(risks → cards)按 `cards.color` 字段分组计数,生成各颜色对应的风险数量统计结果。
在 Laravel 应用中,当需要跨模型聚合统计(如“每种卡片颜色下有多少个风险”),直接使用 Eloquent 关系方法(如 withCount())可能无法满足按父表字段(如 cards.color)分组的需求。此时应结合查询构建器的 join()、select()、groupBy() 及 DB::raw() 实现精准聚合。
正确做法是:*从 risks 表出发,LEFT JOIN 或 INNER JOIN cards 表,选择 cards.color 并按其分组,再使用 `COUNT()` 统计每组风险数**。注意原答案中存在两处关键错误:
- 表别名误写为 card(应为 cards);
- SELECT 中引用了不存在的 card.name(cards 表无 name 字段,且需求只需 color);
- 使用 rightJoin 易导致未关联卡片的风险被遗漏,推荐 leftJoin 或更稳妥的 innerJoin(仅统计有有效卡片的风险)。
✅ 推荐写法(使用 innerJoin,语义清晰、性能良好):
use Illuminate\Support\Facades\DB;
use App\Models\Risk;
$result = Risk::select('cards.color', DB::raw('count(*) as count'))
->join('cards', 'risks.card_id', '=', 'cards.id')
->groupBy('cards.color')
->get();执行后将返回如下集合(Eloquent Collection):
[
{"color": "green", "count": 4},
{"color": "blue", "count": 2},
{"color": "red", "count": 6},
{"color": "yellow","count": 1}
]? 进阶建议:
- 若需包含 cards.color 为 NULL 的风险(即 card_id 无效),改用 leftJoin 并添加 ->whereNotNull('cards.id') 控制逻辑;
- 可封装为 Risk 模型的 本地作用域(Local Scope) 提升复用性:
// In app/Models/Risk.php
public function scopeWithColorCount($query)
{
return $query->select('cards.color', DB::raw('count(*) as count'))
->join('cards', 'risks.card_id', '=', 'cards.id')
->groupBy('cards.color');
}
// 使用
$counts = Risk::withColorCount()->get();⚠️ 注意事项:
- 确保数据库中 cards.id 和 risks.card_id 字段类型一致(如均为 BIGINT UNSIGNED),避免隐式转换导致 JOIN 失效;
- 若 cards.color 允许为 NULL,且需单独统计,可追加 ->orderByRaw('color IS NULL, color') 保证排序可控;
- 在高并发或大数据量场景下,建议为 risks.card_id 添加索引以加速 JOIN 与 GROUP BY。
通过以上方式,即可在 Laravel 中优雅、高效地完成跨模型分组计数,无需脱离 Eloquent 生态直写原生 SQL。










