
本文详解如何将统计“过去 30 天每日浏览量”的原生 sql 转换为 laravel 的 query builder 写法,涵盖日期截取、分组聚合、排序限制等关键点,并指出常见陷阱与最佳实践。
在 Laravel 中,直接使用 DB::table()(即 Query Builder)是处理此类聚合查询最清晰、高效的方式——相比强行套用 Eloquent 模型,它更贴近 SQL 语义,且避免了模型层不必要的开销。
但需注意:您提供的原始答案中存在严重语法错误,会导致查询失败或结果异常。例如:
- ❌ "date ('created_at')" —— 错误的字符串拼接,date() 是 MySQL 函数,不能加引号;
- ❌ groupBy("created_at") —— 按完整时间戳分组 ≠ 按日期分组,将导致每天多条记录(因 created_at 含时分秒);
- ❌ orderBy("created_at","desc") —— 应按日期排序,而非带精度的时间戳,否则分组后排序逻辑错乱;
- ❌ 缺少 DATE() 函数的正确用法及数据库兼容性处理。
✅ 正确写法(兼容 MySQL)如下:
use Illuminate\Support\Facades\DB;
$viewsByDay = DB::table('views')
->selectRaw('DATE(created_at) as day, COUNT(*) as titles')
->where('view_type', 'App\\Title')
->groupBy('day') // 按别名分组(MySQL 支持),或改用 selectRaw 中的表达式
->orderBy('day', 'desc')
->limit(30)
->get();? 关键说明:
- selectRaw() 用于注入原生 SQL 表达式,DATE(created_at) 精确提取日期部分(如 '2024-05-20');
- groupBy('day') 利用 MySQL 对 SELECT 别名的支持,简洁安全;若需跨数据库兼容(如 PostgreSQL),应改为 groupBy(DB::raw('DATE(created_at)'));
- orderBy('day', 'desc') 确保按自然日期倒序排列(最新日优先);
- limit(30) 控制返回条数,符合需求。
? 进阶建议:
- 若项目已定义 View 模型,可配合 toBase() 获取查询构造器,但无实质优势;
- 需补全时间范围(如近 30 天内有数据才显示)?可追加 whereBetween('created_at', [now()->subDays(30), now()]);
- 返回结果中 day 是字符串,如需 Carbon 实例,可在集合中映射转换:->map(fn ($item) => tap($item, fn ($i) => $i->day = \Carbon\Carbon::parse($i->day)))。
总结:Query Builder 是处理复杂聚合查询的首选工具。牢记 selectRaw + groupBy + orderBy 的协同逻辑,避开字符串误引、分组粒度错误等典型坑,即可精准复现原生 SQL 语义。










