Laravel DB查询转换异常:原生SQL有结果但构造器无返回
解决Laravel DB查询构造器转换原生SQL的分组问题
看起来你遇到的核心问题是MySQL的ONLY_FULL_GROUP_BY模式(Laravel默认开启的严格模式包含这个规则)和原生SQL非严格写法之间的冲突,我来一步步帮你分析和解决:
问题根源
你的原生SQL在非严格SQL模式下能运行,但它其实不符合标准SQL的分组规则:你SELECT了id字段,但GROUP BY的是shipmentID。在严格模式下,MySQL要求SELECT列表中的每一列要么是GROUP BY的分组字段,要么是被聚合函数(比如COUNT、MAX)处理过的字段。
而你的Laravel代码出现两种异常的原因:
- 当Laravel自动将
id加入GROUP BY时,每个分组变成了shipmentID + id的组合,每个分组只会有1条记录,自然COUNT(id) > 1的条件无法满足,所以返回空结果。 - 手动移除
id的分组后,触发1055错误,这是严格模式下的正常校验,提示你SELECT的id字段不在分组规则里。
解决方案
根据你的实际需求,有几种可行的调整方式:
1. 仅获取重复的shipmentID和对应的计数(最推荐)
如果你不需要id字段,只需要找出重复的shipmentID及其数量,直接修改select语句,只保留分组字段和聚合函数:
$results = DB::table('agent_billings_items') ->select('shipmentID', DB::raw('count(*) as count')) ->where('agentBillingBatch', $this->id) ->whereNotNull('shipmentID') ->whereNull('deleted_at') ->groupBy('shipmentID') ->having('count', '>', 1) ->get();
2. 获取分组中的一个id(比如最大/最小id)
如果你确实需要每个重复分组里的一个id,可以用聚合函数(如MAX()、MIN())来获取该分组下的某个id:
$results = DB::table('agent_billings_items') ->select(DB::raw('MAX(id) as id'), 'shipmentID', DB::raw('count(*) as count')) ->where('agentBillingBatch', $this->id) ->whereNotNull('shipmentID') ->whereNull('deleted_at') ->groupBy('shipmentID') ->having('count', '>', 1) ->get();
3. 获取分组下的所有id
如果需要该重复shipmentID对应的所有id,可以用GROUP_CONCAT()将多个id拼接成字符串:
$results = DB::table('agent_billings_items') ->select('shipmentID', DB::raw('GROUP_CONCAT(id) as ids'), DB::raw('count(*) as count')) ->where('agentBillingBatch', $this->id) ->whereNotNull('shipmentID') ->whereNull('deleted_at') ->groupBy('shipmentID') ->having('count', '>', 1) ->get();
(不推荐)临时关闭严格模式
如果你坚持要和原生SQL写法一致,可以临时关闭ONLY_FULL_GROUP_BY,但这可能导致数据不一致的风险。在config/database.php的mysql配置中修改:
'strict' => false,
或者更精准地关闭单个规则:
'modes' => [ 'STRICT_TRANS_TABLES', 'NO_ZERO_IN_DATE', 'NO_ZERO_DATE', 'ERROR_FOR_DIVISION_BY_ZERO', 'NO_ENGINE_SUBSTITUTION', ],
(移除ONLY_FULL_GROUP_BY即可)
内容的提问来源于stack exchange,提问作者inuShiva
相关产品推荐
相关产品推荐

