Laravel查询优化:如何获取每家公司最新2条销售报表?
获取每家公司最新2条销售报表的正确实现
你之前的修改思路有个关键问题:把limit(2)加在join闭包里,是对整个关联查询的结果做全局限制,而不是针对每个公司分组后取前2条,所以会返回所有period小于等于最大period的记录,完全达不到每家取2条的效果。
下面给你几种可行的方案,根据你的数据库版本选择最合适的:
方案一:使用窗口函数(推荐,适用于MySQL 8.0+/PostgreSQL等支持窗口函数的数据库)
窗口函数是处理这种分组取Top N场景最直观高效的方式,利用ROW_NUMBER()给每个公司的记录按日期排序后分配行号,再过滤出行号≤2的记录:
$table_data = App\Salesreport::select([ '*', DB::raw('ROW_NUMBER() OVER (PARTITION BY company_id ORDER BY periods DESC) AS row_num') ]) ->having('row_num', '<=', 2) ->get();
补充说明:
PARTITION BY company_id:按公司ID分组,确保行号是每个公司内部单独计算的ORDER BY periods DESC:按日期倒序,最新的记录行号为1,次新为2- 如果你的业务允许同日期的记录都算作"最新"(比如某公司同一天有2条报表,都要保留),可以把
ROW_NUMBER()换成RANK()或DENSE_RANK(),这样同日期的记录会得到相同的行号,不会被过滤掉。
方案二:关联子查询(适用于MySQL 5.7及以下不支持窗口函数的版本)
如果你的数据库不支持窗口函数,可以用关联子查询统计每条记录在同公司中的"最新排名":
$table_data = App\Salesreport::where(function ($query) { $query->selectRaw('COUNT(*)') ->from('salesreport as sr') ->whereColumn('sr.company_id', 'salesreport.company_id') ->where('sr.periods', '>=', DB::raw('salesreport.periods')) ->groupBy('sr.company_id') ->havingRaw('COUNT(*) <= 2'); })->get();
这个逻辑是:对每条记录,统计同公司中日期大于等于它的记录数量,如果数量≤2,说明这条记录属于该公司最新的2条之一。
方案三:使用子查询筛选ID(另一种高效实现)
你也可以通过子查询先找出每个公司最新2条记录的ID,再用主查询获取完整数据:
$table_data = DB::table('salesreport') ->whereIn('id', function ($query) { $query->select('id') ->from('salesreport as sr') ->whereColumn('sr.company_id', 'salesreport.company_id') ->orderBy('sr.periods', 'desc') ->limit(2); }) ->get();
这种方式的优势是子查询只取ID,主查询再关联获取数据,在数据量较大时性能不错,但要注意limit(2)在子查询中对每个分组生效的前提是数据库支持这种关联子查询的写法(大部分现代数据库都支持)。
内容的提问来源于stack exchange,提问作者PamanBeruang
相关产品推荐
相关产品推荐

