Search code examples
sqllaraveleloquentgreatest-n-per-grouplaravel-query-builder

Laravel query Builder Select from subquery


I'm trying to convert a sql query to a Laravel Query builder, but i find hard understand which is the best way for FORM SUBQUERY in Laravel, because if i'm not wrong, is not something that you can normally do whit query builder, but you should implement in raw.

is this right?

and how should translate this?

 SELECT t1.* FROM (//with out sub query it would be just products AS t1
                   SELECT SUM(amount)AS amount, products.* 
                   FROM products ,orders 
                   WHERE orders.product_id = products.product_id 
                   GROUP BY orders.product_id )AS t1
 LEFT OUTER JOIN products as t2 
 ON(t1.name = t2.name AND t1.year_production< t2.year_production) 
 WHERE t2.name is null 

I could translate just with out adding the sub query

 $poductlist =Product::from('products AS 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')
                                    ->orderBy('t2.name','desc');})
                     ->get();

Solution

  • I believe you can rewrite your query as correlated sub query to filter products with max value of year_production where names are duplicate

    select p.product_id,
           p.name,
           coalesce(sum(o.amount),0) as amount
    from products as p
    left join orders o on p.product_id = o.product_id
    where exists (
        select 1
        from products p1
        where p1.name = p.name
        having max(p1.year_production) = p.year_production
    )
    group by p.product_id, p.name
    order by p.product_id
    

    and in query builder you can transform it as

    $products = DB::table('products as p')
                   ->select(['p.product_id',
                             'p.name',
                              DB::raw('coalesce(sum(o.amount),0) as amount')
                            ])
                   ->leftJoin('orders as o', 'p.product_id', '=', 'o.product_id' )
                   ->whereExists(function ($query) {
                       $query->select(DB::raw(1))
                             ->from('products as p1')
                             ->whereRaw(DB::raw('p1.name = p.name'))
                             ->havingRaw('max(p1.year_production) = p.year_production')
                             ;
                  })
                  ->groupBy('p.product_id','p.name')
                  ->orderBy('p.product_id')
                  ->get();