Laravel查询构造器是否需要用原生实现子查询?如何转换带from子查询的SQL
认知纠正
- 你所持有的「Laravel中子查询无法直接通过查询构造器实现、必须编写原生SQL」的认知完全错误。
- 从Laravel 5.6版本开始,框架对全场景子查询提供了原生支持,无论是FROM子句嵌套子查询、JOIN关联嵌套子查询、WHERE条件嵌套子查询,都可以直接通过查询构造器的链式方法实现,无需拼接原生SQL,还能自动享受参数绑定、字段转义的安全保障。
原SQL逻辑拆解
原SQL实现的核心逻辑为:
- 先执行内层子查询,关联products和orders表,按商品ID分组计算每个商品的订单总金额,将分组结果作为临时表t1
- 把临时表t1和products表的自身实例t2做左外连接,匹配规则为「同名商品、且t1的生产年份小于t2的生产年份」
- 最终过滤出没有匹配到t2记录的t1数据,本质是取每个同名商品组中生产年份最早的条目。
对应查询构造器实现
你现有代码的问题
- 完全遗漏了原SQL中FROM位置的分组统计子查询,直接把products表当作了t1,丢失了
SUM(amount)的聚合计算逻辑 - 错将
whereNull('t2.name')过滤条件写在了join闭包的关联条件中,该条件是关联完成后的结果过滤,不属于ON子句的匹配规则,放错位置会导致关联逻辑失效 - 变量名
$poductlist存在拼写偏差,正确拼写为$productList - 原SQL中
SUM(amount)字段存在字段歧义(products和orders表如果都有amount字段会触发SQL执行错误),需要明确指定是orders表的amount字段
正确实现代码
// 先构造FROM子句中嵌套的内层子查询 $innerSubQuery = DB::table('products') ->join('orders', 'orders.product_id', '=', 'products.product_id') ->selectRaw('SUM(orders.amount) AS amount, products.*') ->groupBy('orders.product_id'); // 主查询将内层子查询作为主表t1 $productList = DB::table($innerSubQuery, 't1') ->select('t1.*') ->leftJoin('products AS t2', function ($join) { $join->on('t1.name', '=', 't2.name') ->on('t1.year_production', '<', 't2.year_production'); }) ->whereNull('t2.name') ->get();
补充说明
- 如果需要基于Eloquent模型实现(比如用你之前写的Product模型),只需要将代码中
DB::table的部分替换为Product::query()即可,子查询的传参逻辑完全兼容,不需要额外修改。 - 无需手写原生SQL:Laravel查询构造器会自动处理子查询的别名转义、参数绑定,和原生SQL执行效果完全一致,且能避免SQL注入风险。
内容的提问来源于stack exchange,提问作者Arcaici
相关产品推荐
相关产品推荐

