Laravel Eloquent查询关联表最早日期等于指定值的项目方法
解决Laravel中筛选关联表最早日期等于指定值的项目问题
首先得说为啥你原来的代码报错:WHERE子句里不能直接用聚合函数(比如MIN()),聚合函数得和GROUP BY搭配,而且要放在HAVING子句里或者子查询里,这就是你碰到1111 Invalid use of group function错误的原因。
下面给你几种更简洁高效的实现方式,不用拆分两次查询:
方法一:在whereHas中嵌套子查询
直接在关联查询的闭包里用子查询判断当前项目的最早日期是否等于目标值,整个逻辑都在一个Eloquent查询里完成:
$targetDate = '2020-01-25'; $projects = Project::whereHas('dates', function ($query) use ($targetDate) { $query->whereRaw('date = (SELECT MIN(date) FROM project_dates WHERE project_id = projects.id)') ->where('date', $targetDate); })->get();
这个逻辑是:先定位当前项目的最早日期,再判断该日期是否匹配你指定的2020-01-25,符合条件的项目才会被筛选出来。
方法二:用havingRaw配合关联查询
另一种思路是先按项目分组,再用HAVING筛选最早日期匹配的项目,更贴近原生SQL的聚合筛选逻辑:
$targetDate = '2020-01-25'; $projects = Project::join('project_dates', 'projects.id', '=', 'project_dates.project_id') ->groupBy('projects.id', 'projects.name') // 注意要包含projects表所有查询字段,避免ONLY_FULL_GROUP_BY报错 ->havingRaw('MIN(project_dates.date) = ?', [$targetDate]) ->select('projects.*') ->get();
如果你的MySQL开启了ONLY_FULL_GROUP_BY(默认开启),GROUP BY后面必须包含SELECT子句里所有非聚合字段,所以这里要把projects.id和projects.name都加上。
方法三:利用Eloquent的withExists(Laravel 8+)
如果你用的是Laravel 8及以上版本,可以用withExists写出更简洁的查询,可读性更强:
$targetDate = '2020-01-25'; $projects = Project::whereExists(function ($query) use ($targetDate) { $query->select(DB::raw(1)) ->from('project_dates') ->whereColumn('project_dates.project_id', 'projects.id') ->havingRaw('MIN(date) = ?', [$targetDate]) ->groupBy('project_id'); })->get();
这个方法本质也是子查询,但whereExists的写法更贴合Eloquent风格,性能也不错——因为EXISTS子查询只要找到匹配项就会停止检索。
这几种方法都能正确筛选出最早日期等于2020-01-25的项目(也就是id为1的Test项目),且无需拆分两次查询。
内容的提问来源于stack exchange,提问作者SuperDJ
相关产品推荐
相关产品推荐

