Laravel开启database.php strict=true时,如何修改查询语句实现未完全分配交易的查询需求?
解决Laravel严格模式下关联查询的GROUP BY问题
遇到这个问题很常见——开启strict模式后,MySQL的only_full_group_by规则会严格校验GROUP BY子句和SELECT字段的对应关系,原查询里的whereHas子查询默认用select *,包含了transaction_allocations.id等不在GROUP BY中的非聚合字段,所以触发了1055错误。
不用关闭严格模式,我们可以通过修改查询语句来适配规则,下面给你两种可行的方案:
方案一:修正whereHas子查询的字段选择
原查询的问题出在whereHas的子查询里,我们只需要明确选择分组字段,并调整HAVING子句的逻辑即可:
Transaction::where('contact_id', 9) ->with('allocations') ->whereHas("allocations", function ($query) { // 只选择分组需要的transaction_id,避免select *带来的字段不匹配问题 $query->select('transaction_id') ->havingRaw('(SELECT credit FROM transactions WHERE id = transaction_id) > sum(amount)') ->groupBy('transaction_id'); }) ->orDoesntHave("allocations") ->get();
为什么这样改?
- 子查询现在只返回
transaction_id,和GROUP BY的字段完全一致,符合only_full_group_by的要求 - HAVING子句通过嵌套查询获取对应交易的
credit值,保持了原有的比较逻辑
方案二:改用LEFT JOIN + GROUP BY(更高效简洁)
如果想简化查询逻辑,用LEFT JOIN一次性处理“有分配但未完全分配”和“无分配”两种情况,性能也会更好:
Transaction::where('contact_id', 9) ->with('allocations') ->leftJoin('transaction_allocations', 'transactions.id', '=', 'transaction_allocations.transaction_id') // 严格模式下,SELECT中的非聚合字段必须出现在GROUP BY里 ->groupBy('transactions.id', 'transactions.credit') ->havingRaw('COALESCE(SUM(transaction_allocations.amount), 0) < transactions.credit OR COUNT(transaction_allocations.id) = 0') ->get();
逻辑说明:
LEFT JOIN会保留所有交易记录,包括没有分配的COALESCE(SUM(...), 0)处理无分配记录的情况(此时SUM结果为null,转成0)COUNT(transaction_allocations.id) = 0直接匹配没有关联分配的交易- GROUP BY包含
transactions.id和transactions.credit,因为我们在SELECT和HAVING里用到了这两个非聚合字段,符合严格模式要求
原查询报错的核心原因
当你使用whereHas时,Laravel默认生成的子查询是select * from transaction_allocations ... group by transaction_id,但SELECT列表里的id、bill_id等字段既不在GROUP BY里,也没有用聚合函数包裹,违反了only_full_group_by的规则——严格模式下,MySQL要求所有非聚合的SELECT字段必须是GROUP BY的一部分,或者函数依赖于GROUP BY字段(比如主键)。
内容的提问来源于stack exchange,提问作者Lucid Polygon
相关产品推荐
相关产品推荐

