将具有许多连接的复杂 SQL 查询转换为 Eloquent

Posted

技术标签:

【中文标题】将具有许多连接的复杂 SQL 查询转换为 Eloquent【英文标题】:Convert complex SQL query with many joins to Eloquent 【发布时间】:2019-08-15 11:03:03 【问题描述】:

我有一个有效的 SQL 查询,这是 *** 成员慷慨赠予我的,它可以在 Laravel 中运行:

$actives = DB::select('SELECT a.* ,COALESCE(b.sold,0), sold
                      FROM ( SELECT * FROM products p 
                      WHERE p.product_type_id = 1
                      AND DATE(p.visible_date) <= ? 
                      AND p.active = 1 
                      AND (p.end_date >= ? or p.end_date IS NULL) ) a 
                      LEFT JOIN ( SELECT product_id , SUM(quantity) sold 
                      FROM order_items oi JOIN orders o 
                      ON o.id = oi.order_id 
                      AND o.paid is not null 
                      GROUP BY product_id ) b
                      ON b.product_id = a.id', [Carbon::now(), Carbon::now()]);

但这会返回一个数组,我正在寻找一个集合。我不能使用selectRaw,因为该方法总是在查询末尾添加FROM tablename

我已将 a 和 b 分成子查询,这两个都有效:

$a = DB::table('products')
         ->where('product_type_id', 1)
         ->whereDate('visible_date', '<=', Carbon::now())
         ->where('active', true)
         ->where(function ($query) 
             $query->where('end_date', '>=', Carbon::now())
                   ->orWhere('end_date', null);  
                   );

$b = DB::table('order_items')
         ->selectRaw('product_id , SUM(quantity) sold')
         ->join('orders', 'orders.id', '=', 'order_items.order_id')
         ->whereNotNull('orders.paid')
          ->groupBy('product_id');

Eloquent 查询构建器有一个 leftJoinSub 函数,但文档似乎表明它只适用于将一个查询与一个子查询连接起来,而我似乎需要在两个子查询上连接一个新查询。

有人知道如何将原始 SQL 放入 Eloquent 中,以便我返回 Products 集合而不是数组吗?

【问题讨论】:

【参考方案1】:

有一种更简单的方法可以减少数据库的资源消耗

我想你已经声明了ProductOrder之间的关系顺序

Class Product extends Model

    public function getSoldAttribute()
    
        return $this->orderItems()
            ->select(\DB::raw('SUM(quantity) as sold'))
            ->join('orders', 'order_items.order_id', '=', 'orders.id')
            ->whereNotNull('orders.paid')
            ->groupBy('product_id')
            ->value('sold');
    
...

然后您使用您想要的过滤器获取您的产品并访问$product-&gt;sold 属性

$products = Products::where('product_type_id', 1)
     ->whereDate('visible_date', '<=', Carbon::now())
     ->where('active', true)
     ->where(function ($query) 
         $query->where('end_date', '>=', Carbon::now())
               ->orWhere('end_date', null);  
     )->get();
foreach($products as $product) 
    var_dump($product->sold);

您还可以使用protected $appends = ['sold']; 强制加载Product 模型中的sold 属性

【讨论】:

我在ProductOrder 之间没有关系。 OrderOrderItem 之间存在关系,ProductOrderItem 之间存在关系。我应该创建一个新的 hasManyThrough 吗? 这看起来正是我想要的,但我遇到了错误。首先,我得到Call to undefined method Illuminate\Database\Query\Expression::groupBy()。另一篇文章说将它移到select 之前,我已经完成了,现在我得到了Call to undefined method Illuminate\Database\Query\Expression::value() @Jacey 你做了return $this-&gt;orders-&gt;select( 而不是return $this-&gt;orders()-&gt;select(。您可以将orders 关系添加到您的问题吗? 我按照您写的方式复制了它,顺便说一句,它缺少括号,但我更正了。我添加了关系public function orders() return $this-&gt;hasManyThrough('App\Order', 'App\OrderItem'); 是的。这实际上也不是我想要的订单。我想要 Orders.paid 不为空的那个产品 id 的 OrderItems 的总和。

以上是关于将具有许多连接的复杂 SQL 查询转换为 Eloquent的主要内容,如果未能解决你的问题,请参考以下文章

如何将在左连接中具有连接的 sql 转换为查询构建器?

我应该将此方法的查询转换为存储过程吗? [关闭]

具有复杂连接的sql查询

如何执行 SQL 游标转换

SQL 查询中表的复杂连接

如何将复杂的 SQL 查询转换为 JPQL?