Laravel原生查询可嵌套子查询吗?我的查询报1055错误求排查
当然可以在Laravel的原生查询里嵌套子查询!你的思路方向是对的,但当前报错是MySQL严格模式和GROUP BY规则导致的,咱们来一步步梳理问题和解决方案:
错误原因分析
你遇到的错误 SQLSTATE[42000]: Syntax error or access violation: 1055 'tenderpo_2019.ac.bid_invitation_id' isn't in GROUP BY,本质是MySQL默认开启了ONLY_FULL_GROUP_BY严格模式。这个模式有个核心要求:SELECT语句中出现的非聚合函数列,必须全部包含在GROUP BY子句中。
你的查询里,SELECT中的子查询用到了ac.bid_invitation_id,但这个字段并没有出现在GROUP BY的method列表里。当你按method分组时,同一个method可能对应多个不同的ac.bid_invitation_id,MySQL无法确定应该用哪个值来执行子查询,所以触发了这个错误。
解决方案
方案1:将子查询改为与分组维度匹配
把原本针对单个bid_invitation_id的统计,改成针对整个method分组的统计,确保子查询和GROUP BY的维度一致:
DB::table('awarded_contracts as ac') ->select( DB::raw("COUNT(ac.id) AS numOfContracts"), DB::raw("SUM(ac.contract_amount) AS sumOfContractAmounts"), DB::raw("(SELECT COUNT(bs.id) FROM bid_submissions bs JOIN bid_invitations bi2 ON bs.bid_invitation_id = bi2.id JOIN procurement_plan_entries ppe2 ON bi2.procurement_plan_entry_id = ppe2.id LEFT JOIN procurement_methods pm2 ON ppe2.procurement_method_id = pm2.id LEFT JOIN procurement_methods pm3 ON bi2.procurement_method_id = pm3.id WHERE (CASE WHEN bi2.procurement_method_id = '0' THEN pm2.title ELSE pm3.title END) = method) AS numOfBidSubmissions"), DB::raw("(CASE WHEN bi.procurement_method_id ='0' THEN pm.title ELSE pm2.title END) as method") ) ->join('bid_invitations as bi', 'bi.id', '=', 'ac.bid_invitation_id') ->leftJoin('procurement_plan_entries as ppe', 'ppe.id', '=', 'bi.procurement_plan_entry_id') ->join('procurement_methods as pm', 'ppe.procurement_method_id', '=', 'pm.id') ->leftJoin('procurement_methods as pm2', 'pm2.id', '=', 'bi.procurement_method_id') ->groupBy('method') ->get();
方案2:预统计投标数后关联(更高效推荐)
先通过子查询预统计每个bid_invitation_id对应的投标数量,再将这个临时表关联到主查询中,避免在SELECT子句中嵌套子查询的问题,同时查询效率更高:
DB::table('awarded_contracts as ac') ->select( DB::raw("COUNT(ac.id) AS numOfContracts"), DB::raw("SUM(ac.contract_amount) AS sumOfContractAmounts"), DB::raw("SUM(COALESCE(bs_count.total, 0)) AS numOfBidSubmissions"), // 按method汇总投标数 DB::raw("(CASE WHEN bi.procurement_method_id ='0' THEN pm.title ELSE pm2.title END) as method") ) ->join('bid_invitations as bi', 'bi.id', '=', 'ac.bid_invitation_id') ->leftJoin('procurement_plan_entries as ppe', 'ppe.id', '=', 'bi.procurement_plan_entry_id') ->join('procurement_methods as pm', 'ppe.procurement_method_id', '=', 'pm.id') ->leftJoin('procurement_methods as pm2', 'pm2.id', '=', 'bi.procurement_method_id') // 关联预统计的投标数临时表 ->leftJoin( DB::table('bid_submissions') ->select('bid_invitation_id', DB::raw('COUNT(id) as total')) ->groupBy('bid_invitation_id') ->getQuery(), // 获取原生查询构建器实例用于关联 'bs_count.bid_invitation_id', '=', 'ac.bid_invitation_id' ) ->groupBy('method') ->get();
不推荐的临时方案:关闭ONLY_FULL_GROUP_BY
虽然可以通过修改MySQL配置或在查询前执行SET sql_mode=(SELECT REPLACE(@@sql_mode,'ONLY_FULL_GROUP_BY',''));来关闭该模式,但这会导致查询结果的不确定性(MySQL会随机选择分组内的字段值),所以不建议在生产环境使用。
内容的提问来源于stack exchange,提问作者Cengkuru Michael

