Laravel Eloquent按slug分组:优先选取非空client_id记录
Laravel Eloquent 按Slug分组优先选取Client_id非空记录的实现
针对你的需求——按slug分组时优先保留client_id非空的记录,无符合条件的记录则保留client_id为null的记录,你当前的代码存在两个核心问题:
groupBy包含了id、name等唯一/非分组字段,导致每个记录都会被单独分组,完全起不到按slug去重的作用;latest()未指定排序优先级,无法实现优先选取client_id非空记录的逻辑。
下面提供两种可靠的实现方案:
方案一:利用排序+分组(适用于MySQL关闭ONLY_FULL_GROUP_BY或兼容模式)
通过排序让client_id非空的记录排在每组最前面,再按slug分组取每组第一条:
Field::select('slug', 'id', 'name', 'field_type') ->where(function($q) use($clientModel) { $q->whereNull('client_id')->orWhere('client_id', $clientModel->id); }) ->orderByRaw('client_id IS NULL, id DESC') // 先把client_id非空的排到前面,再按id倒序 ->groupBy('slug') ->paginate(20);
注:如果你的MySQL开启了ONLY_FULL_GROUP_BY模式,这种写法会触发SQL报错,建议使用方案二。
方案二:窗口函数实现(推荐,MySQL 8.0+)
使用ROW_NUMBER()窗口函数给每个slug分组的记录编号,让client_id非空的记录编号为1,最后筛选编号为1的记录:
Field::select('slug', 'id', 'name', 'field_type') ->from(function($query) use($clientModel) { $query->select('*', \DB::raw('ROW_NUMBER() OVER (PARTITION BY slug ORDER BY client_id IS NULL ASC) as rn') )->where(function($q) use($clientModel) { $q->whereNull('client_id')->orWhere('client_id', $clientModel->id); }); }, 'sub') ->where('rn', 1) ->paginate(20);
逻辑说明:
PARTITION BY slug按slug对记录分组;ORDER BY client_id IS NULL ASC中,client_id IS NULL对非空记录返回0,空记录返回1,升序排序后非空记录会排在每组的最前面,编号为1;- 最终筛选
rn=1的记录,就实现了优先取client_id非空、无则取空的需求。
内容的提问来源于stack exchange,提问作者Vinod Patidar
相关产品推荐
相关产品推荐

