不使用GROUP BY、UNIQUE或DISTINCT,如何在Laravel中实现数据分组?
Laravel分组查询替代方案(避免关闭strict模式)
关于strict模式的疑问
你并没有多虑:Laravel数据库配置中的strict模式包含only_full_group_by规则,这是符合SQL标准的约束,目的是避免分组查询返回不确定的结果(比如随机选取未分组字段的值)。保持strict模式开启能减少潜在的逻辑bug,不建议关闭。
替代方案
你的需求是按company_agreement_id分组,同时获取对应记录的id,这里需要明确:每个company_agreement_id对应多条记录时,你需要哪一条的id?以下是两种常见场景的实现方式:
场景1:取每个分组下最新的id(按id倒序取第一条)
使用窗口函数ROW_NUMBER()可以精准控制选取分组内的指定记录,符合SQL标准且无需关闭strict模式:
原生SQL写法
SELECT id, company_agreement_id FROM ( SELECT id, company_agreement_id, ROW_NUMBER() OVER (PARTITION BY company_agreement_id ORDER BY id DESC) AS rn FROM company_agreement_historicals WHERE ((expiration_date > ? and status = ?) or status = ?) ) AS sub WHERE rn = 1;
Laravel Query Builder写法
$test = DB::table('company_agreement_historicals') ->select('id', 'company_agreement_id') ->fromSub(function ($query) { $query->select( 'id', 'company_agreement_id', DB::raw('ROW_NUMBER() OVER (PARTITION BY company_agreement_id ORDER BY id DESC) AS rn') )->where(function ($q) { $q->where('expiration_date', '>', '2023-09-04 11:19:05') ->where('status', 2); })->orWhere('status', 1); }, 'sub') ->where('rn', 1) ->get();
场景2:取每个分组下最小的id(或最大id)
如果只需要分组内的最小/最大id,可以直接用聚合函数配合GROUP BY,这也是符合SQL标准的写法:
原生SQL写法
SELECT MIN(id) AS id, company_agreement_id FROM company_agreement_historicals WHERE ((expiration_date > ? and status = ?) or status = ?) GROUP BY company_agreement_id;
Laravel Query Builder写法
$test = DB::table('company_agreement_historicals') ->select(DB::raw('MIN(id) AS id'), 'company_agreement_id') ->where(function ($q) { $q->where('expiration_date', '>', '2023-09-04 11:19:05') ->where('status', 2); }) ->orWhere('status', 1) ->groupBy('company_agreement_id') ->get();
为什么原SQL有问题
原SQL在开启only_full_group_by时会报错,因为SELECT中的id字段既不在GROUP BY列表中,也没有使用聚合函数,数据库无法确定应该返回分组内哪一条记录的id,返回结果是不确定的。严格模式下禁止这种写法,正是为了避免这种逻辑模糊的问题。
内容的提问来源于stack exchange,提问作者Alexander
相关产品推荐
相关产品推荐

