Laravel Fluent查询生成器与子查询连接

lztngnrs  于 2022-12-14  发布在  其他
关注(0)|答案(8)|浏览(127)

好吧,经过几个小时的研究,仍然使用DB::select我必须问这个问题。因为我即将扔掉我的电脑;).
我想获取用户的最后一次输入(基于时间戳)。

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上的另一个帖子中得到了这个查询。
我已经尝试了一切来做这与流畅的查询构建器在Laravel然而没有成功。
我知道手册上说你可以这样做:

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

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

lawou6xi

lawou6xi1#

好吧,你们所有人都在绝望中来到这里寻找同样的问题。我希望你们会发现这个更快,然后我没有; O的。
JoostK在github上告诉我“要连接的第一个参数是你要连接的表(或数据)",他是对的。
下面是代码。不同的表和名称,但你会得到的想法,对吗?它t

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();
fumotvh3

fumotvh32#

我在寻找一个解决相关问题的方法:查找每个组的最新记录,该组是N = 1的典型greatest-n-per-group的专门化。
解决方案涉及到您在这里处理的问题(即,如何在Eloquent中构建查询),所以我将其发布,因为它可能对其他人有帮助。它演示了一种更简洁的子查询构造方法,使用功能强大的Eloquent Fluent接口,带有多个连接列和连接子选择中的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子查询连接,因此有一种构建查询的本地方式。

$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();
6rqinv9w

6rqinv9w3#

我想你要找的是“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.*']);
31moq8wy

31moq8wy4#

在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();
kq4fsx7k

kq4fsx7k5#

我在Laravel 7.25,我不知道它是否支持以前的版本或没有,但它相当不错。

函数的语法:

public function joinSub($query, $as, $first, $operator = null, $second = null, $type = 'inner', $where = false)
示例:
显示/获取用户ID和他们留下的帖子总数,连接两个表用户和帖子。

return DB::table('users')
            ->joinSub('select user_id,count(id) noOfPosts from posts group by user_id', 'totalPosts', 'users.id', '=', 'totalPosts.user_id', 'left')
            ->select('users.name', 'totalPosts.noOfPosts')
            ->get();

备选方案:

如果您不想在leftjoin中使用'left',则可以使用另一个预建函数

public function leftJoinSub($query, $as, $first, $operator = null, $second = null)
    {
        return $this->joinSub($query, $as, $first, $operator, $second, 'left');
    }

是的,它实际上调用了相同的函数,但是它传递的是连接类型本身。你可以对其他连接应用相同的逻辑,比如rightJoinSub(...)等等。

fquxozlt

fquxozlt6#

我不能发表评论,因为我的声誉不够高。@富兰克林·里韦罗如果你使用的是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());
jaql4c8m

jaql4c8m7#

您可以使用以下插件来处理laravel 5.5+中所有与子查询相关的函数
https://github.com/maksimru/eloquent-subquery-magic

User::selectRaw('user_id,comments_by_user.total_count')->leftJoinSubquery(
  //subquery
  Comment::selectRaw('user_id,count(*) total_count')
      ->groupBy('user_id'),
  //alias
  'comments_by_user', 
  //closure for "on" statement
  function ($join) {
      $join->on('users.id', '=', 'comments_by_user.user_id');
  }
)->get();
crcmnpdw

crcmnpdw8#

对于我的情况,我找到了另一种解决方案,那就是用子查询来指定查询,以避免歧义。

Property::joinSub(Agency::select('id', 'subscription', )->getQuery(), 'agencies', 'agencies.id', 'properties.agency_id')

相关问题