
本文介绍如何使用 laravel eloquent 一次性获取各状态的应用数量及总记录数,避免多次查询,通过 left join + group by + union all 实现“all”行与各状态行的完整统计表。
本文介绍如何使用 laravel eloquent 一次性获取各状态的应用数量及总记录数,避免多次查询,通过 left join + group by + union all 实现“all”行与各状态行的完整统计表。
在实际开发中,常需统计关联数据(如应用按状态分类)并同时展示汇总总数(如“All”行)。若对每个状态单独调用 count(),不仅代码冗余、性能低下(N+1 查询风险),还难以动态扩展新状态。Laravel 提供了更高效、可维护的解决方案——利用 LEFT JOIN 确保所有状态(含零记录)均被包含,并借助 UNION ALL 合并总计行。
✅ 推荐实现:单次查询返回完整结果集
use Illuminate\Support\Facades\DB;
use App\Models\Status;
$results = DB::table('status')
->select(
DB::raw("'All' as status_name"),
DB::raw('COUNT(applications.id) as total')
)
->join(DB::raw('(SELECT COUNT(*) as cnt FROM applications WHERE is_active = true) AS total_table'), DB::raw('1'), '=', DB::raw('1'))
->unionAll(
DB::table('status')
->select(
DB::raw('UPPER(LEFT(status.desc, 1)) || LOWER(SUBSTR(status.desc, 2)) as status_name'),
DB::raw('COUNT(applications.id) as total')
)
->leftJoin('applications', function ($join) {
$join->on('status.id', '=', 'applications.status_id')
->where('applications.is_active', '=', true);
})
->groupBy('status.id', 'status.desc')
)
->orderByRaw("CASE WHEN status_name = 'All' THEN 0 ELSE 1 END")
->orderBy('status_name')
->get();
? 说明:
Laravel Cloud下载Laravel Cloud(cloud.laravel.com)。此技能用于处理所有 Laravel Cloud 请求,包括搜索和读取数据。只要任务涉及 Laravel Cloud,即使用此技能。
- 使用 LEFT JOIN(而非 INNER JOIN)确保 status 表中所有状态(包括 processed/failed 等无匹配应用的记录)均出现在结果中;
- COUNT(applications.id) 在 LEFT JOIN 下天然返回 0(因无匹配行时 applications.id 为 NULL);
- UNION ALL 将总计行('All')与各状态行合并,避免重复扫描;
- orderByRaw 确保 'All' 行始终置顶;
- UPPER(LEFT(...)) 实现首字母大写格式化(如 completed → Completed),适配 Blade 展示需求。
? Blade 模板渲染示例(resources/views/applications/stats.blade.php)
| Status | Total |
|---|---|
| {{ $row->status_name }} | {{ $row->total }} |
⚠️ 注意事项
- 模型关系优化建议:若已在 Status 模型中定义 applications() 关系(hasManyThrough 或反向 belongsTo),可改用 Eloquent 的 withCount() 配合 map() 构造结果,但原生查询在复杂聚合场景下更可控、性能更优;
- 索引检查:确保 applications.status_id 和 applications.is_active 字段存在联合索引(如 INDEX status_id_is_active (status_id, is_active)),大幅提升 JOIN 效率;
- 空状态处理:本方案默认包含所有 status 记录;若仅需活跃状态,将 LEFT JOIN 改为 INNER JOIN 并移除 WHERE 中的 is_active 条件至主查询即可;
- 数据库兼容性:上述 SQL 使用 PostgreSQL 风格字符串拼接(||),若使用 MySQL,请替换为 CONCAT(UCASE(LEFT(status.desc, 1)), LCASE(SUBSTRING(status.desc, 2)))。
该方案以一次数据库往返完成全部统计,逻辑清晰、扩展性强,是 Laravel 中处理“分类统计 + 总计”场景的标准实践。











