首页 文章

Laravel Fluent Query Builder加入子查询

提问于
浏览
57

好的经过几个小时的研究并仍在使用DB :: select我不得不问这个问题 . 因为我即将把我的电脑拉下来;) .

我想得到用户的最后一个输入(基于时间戳) . 我可以使用原始sql执行此操作

SELECT  c.*, p.*
FROM    users c INNER JOIN
(
  SELECT  user_id,
          MAX(created_at) MaxDate
  FROM    `catch-text`
  GROUP BY user_id
 ) MaxDates ON c.id = MaxDates.user_id INNER JOIN
    `catch-text` p ON   MaxDates.user_id = p.user_id
     AND MaxDates.MaxDate = p.created_at

我从stackoverflow上的另一个帖子here得到了这个查询 .

我已尝试使用Laravel中的流畅查询构建器完成所有操作,但没有成功 .

我知道手册说你可以这样做:

DB::table('users')
    ->join('contacts', function($join)
    {
        $join->on('users.id', '=', 'contacts.user_id')->orOn(...);
    })
    ->get();

但这没有多大帮助,因为我不知道如何在那里使用子查询?谁可以点亮我的一天?

5 回答

  • 0

    对你们所有人来说,绝望地到达这里寻找同样的问题 . 我希望你能比我更快找到它; O.

    这是如何解决的 . JoostK在github告诉我“加入的第一个参数是你加入的表格(或数据) . ”而他是对的 .

    这是代码 . 不同的表和名称,但你会得到这个想法吗?它

    DB::table('users')
            ->select('first_name', 'TotalCatches.*')
    
            ->join(DB::raw('(SELECT user_id, COUNT(user_id) TotalCatch,
                   DATEDIFF(NOW(), MIN(created_at)) Days,
                   COUNT(user_id)/DATEDIFF(NOW(), MIN(created_at))
                   CatchesPerDay FROM `catch-text` GROUP BY user_id)
                   TotalCatches'), 
            function($join)
            {
               $join->on('users.id', '=', 'TotalCatches.user_id');
            })
            ->orderBy('TotalCatches.CatchesPerDay', 'DESC')
            ->get();
    
  • 11

    我正在寻找一个相关问题的解决方案:找到每组的最新记录,这是N = 1的典型greatest-n-per-group的特化 .

    解决方案涉及您在此处理的问题(即,如何在Eloquent中构建查询),因此我发布它,因为它可能对其他人有帮助 . 它演示了一种更简洁的子查询构造方式,使用功能强大的Eloquent流畅接口和多个连接列以及连接子选择内的 where 条件 .

    在我的示例中,我想获取由 watch_id 标识的每个组的最新DNS扫描结果(表 scan_dns ) . 我分别构建子查询 .

    我希望Eloquent生成的SQL:

    SELECT * FROM `scan_dns` AS `s`
    INNER JOIN (
      SELECT x.watch_id, MAX(x.last_scan_at) as last_scan
      FROM `scan_dns` AS `x`
      WHERE `x`.`watch_id` IN (1,2,3,4,5,42)
      GROUP BY `x`.`watch_id`) AS ss
    ON `s`.`watch_id` = `ss`.`watch_id` AND `s`.`last_scan_at` = `ss`.`last_scan`
    

    我是通过以下方式完成的:

    // table name of the model
    $dnsTable = (new DnsResult())->getTable();
    
    // groups to select in sub-query
    $ids = collect([1,2,3,4,5,42]);
    
    // sub-select to be joined on
    $subq = DnsResult::query()
        ->select('x.watch_id')
        ->selectRaw('MAX(x.last_scan_at) as last_scan')
        ->from($dnsTable . ' AS x')
        ->whereIn('x.watch_id', $ids)
        ->groupBy('x.watch_id');
    $qqSql = $subq->toSql();  // compiles to SQL
    
    // the main query
    $q = DnsResult::query()
        ->from($dnsTable . ' AS s')
        ->join(
            DB::raw('(' . $qqSql. ') AS ss'),
            function(JoinClause $join) use ($subq) {
                $join->on('s.watch_id', '=', 'ss.watch_id')
                     ->on('s.last_scan_at', '=', 'ss.last_scan')
                     ->addBinding($subq->getBindings());  
                     // bindings for sub-query WHERE added
            });
    
    $results = $q->get();
    

    更新:

    Laravel 5.6.17以来,添加了sub-query joins,因此有一种本机方式来构建查询 .

    $latestPosts = DB::table('posts')
                       ->select('user_id', DB::raw('MAX(created_at) as last_post_created_at'))
                       ->where('is_published', true)
                       ->groupBy('user_id');
    
    $users = DB::table('users')
            ->joinSub($latestPosts, 'latest_posts', function ($join) {
                $join->on('users.id', '=', 'latest_posts.user_id');
            })->get();
    
  • 1

    在Laravel中查询子查询

    $resortData = DB::table('resort')
            ->leftJoin('country', 'resort.country', '=', 'country.id')
            ->leftJoin('states', 'resort.state', '=', 'states.id')
            ->leftJoin('city', 'resort.city', '=', 'city.id')
            ->select('resort.*', 'country.name as country_name', 'states.name as state_name','city.name as city_name', DB::raw("(SELECT GROUP_CONCAT(amenities.name) from resort_amenities LEFT JOIN amenities on amenities.id= resort_amenities.amenities_id WHERE resort_amenities.resort_id=resort.id) as amenities_name"))->groupBy('resort.id')
            ->orderBy('resort.id', 'DESC')
           ->get();
    
  • 124

    我想你要找的是"joinSub" . 它得到laravel ^ 5.6的支持 . 如果您使用低于5.6的laravel版本,您还可以在应用服务提供商文件中将其注册为宏 . 喜欢这个https://github.com/teamtnt/laravel-scout-tntsearch-driver/issues/171#issuecomment-413062522

    $subquery = DB::table('catch-text')
                ->select(DB::raw("user_id,MAX(created_at) as MaxDate"))
                ->groupBy('user_id');
    
    $query = User::joinSub($subquery,'MaxDates',function($join){
              $join->on('users.id','=','MaxDates.user_id');
    })->select(['users.*','MaxDates.*']);
    
  • 1

    我无法评论,因为我的声誉不够高 . @Franklin Rivero如果您使用的是Laravel 5.2,则可以使用setBindings方法在主查询而不是连接上设置绑定 .

    所以@ ph4r05的答案中的主要查询看起来像这样:

    $q = DnsResult::query()
        ->from($dnsTable . ' AS s')
        ->join(
            DB::raw('(' . $qqSql. ') AS ss'),
            function(JoinClause $join) {
                $join->on('s.watch_id', '=', 'ss.watch_id')
                     ->on('s.last_scan_at', '=', 'ss.last_scan');
            })
        ->setBindings($subq->getBindings());
    

相关问题