简体   繁体   English

具有自定义选择/联接/顺序的Laravel雄辩模型查询

[英]Laravel Eloquent Model query with custom select/join/order

I have models called Post, and PostView. 我有称为Post和PostView的模型。 A Post can have many PostViews. 一个帖子可以有多个PostView。 I need to get a paginated list of Posts ordered by the count of PostViews created within the last 30 days. 我需要获取按过去30天内创建的PostViews数量排序的帖子分页列表。

I can do this using withCount : 我可以使用withCount来做到这一点:

// works but S L O W
$posts = Post::withCount(['views' => function($query) {
            $query->where('post_views.created_at', '>=', 'DATE_ADD(CURDATE(), INTERVAL -30 DAY)');
         }])
         ->orderBy('views_count')
         ->paginate(10);

However this generates a query that is really slow, taking ~24 seconds. 但是,这会产生非常慢的查询,大约需要24秒。

Using raw sql I can get the correct results much more efficiently, how can I turn that into the paginated model collection? 使用原始sql可以更有效地获取正确的结果,如何将其转换为分页的模型集合?

This generates the correct query to grab the first 10, but the resulting collection is empty. 这将生成正确的查询以获取前10个查询,但结果集合为空。 I assume it has something to do with the selectRaw 我认为这与selectRaw

$posts = Post::selectRaw('posts.*, count(post_views.id) as views_count')
           ->join('post_views', function($join) {
                $join->on('posts.id', '=', 'post_views.post_id')
                   ->where('post_views.created_at', '>=', 'DATE_ADD(CURDATE(), INTERVAL -30 DAY)');
            })
            ->groupBy('posts.id')
            ->orderBy('views_count', 'DESC')
            ->take(10)
            ->get();

If I run the query that generates directly in mysql I do get results: (note - truncated to posts.id for brevity) 如果我运行直接在mysql中生成的查询,我会得到结果:(注意-为简洁起见,将其截断为posts.id)

mysql> select posts.id, count(post_views.id) as views_count from `posts` inner join `post_views` on `posts`.`id` = `post_views`.`post_id` and `post_views`.`created_at` >= DATE_ADD(CURDATE(), INTERVAL -30 DAY) group by `posts`.`id` order by `views_count` desc limit 10;
+--------+-------------+
| id     | views_count |
+--------+-------------+
| 150277 |          22 |
|  43843 |           6 |
| 138789 |           4 |
| 180565 |           4 |
|  50555 |           3 |
|   2679 |           3 |
| 188572 |           3 |
| 217454 |           3 |
| 136736 |           3 |
| 126472 |           2 |
+--------+-------------+
10 rows in set (1.26 sec)

Any help is appreciated, thanks. 任何帮助表示赞赏,谢谢。

The query should be : 查询应为:

$posts = Post::selectRaw('posts.*, count(post_views.id) as views_count')
           ->join('post_views', function($join) {
                $join->on('posts.id', '=', 'post_views.post_id')
                   ->where('post_views.created_at', '>=', 'DATE_ADD(CURDATE(), INTERVAL -30 DAY)');
            })
            ->groupBy('posts.id')
            ->orderBy('views_count', 'DESC')
            // ->take(10) // no need of this
            //->get(); // will be using paginate
             ->paginate(10);

Have you tried 你有没有尝试过

PostViews::where('created_at', '>=', 'DATE_ADD(CURDATE(), INTERVAL -30 DAY)')->groupBy('post_id')->selectRaw('count(*) as post_count')->with('post')->paginate(10);

Assumming you have an index on created_at 假设您在created_at上有一个索引

I have figured this out - the join->where clause was getting escaped and invalidating the query. 我已经弄清楚了-join-> where子句被转义并使查询无效。 Not invalid sql, just an invalid comparison such that there would always be no results. 不是无效的sql,只是无效的比较,这样永远不会有结果。

Of course the query logger didn't show that it was escaped, so copying the query into mysql worked, making this far more difficult to track down than it should have been. 当然,查询记录器没有显示出它已被转义,因此将查询复制到mysql上是可行的,这使得查找该记录比原本要困难得多。

This works: 这有效:

$posts = Post::select('posts.*', DB::raw('count(post_views.id) as views_count'))
->join('post_views', function($join) {
    $join->on('posts.id', '=', 'post_views.post_id')
        ->where('post_views.created_at', '>=', DB::raw('DATE_ADD(CURDATE(), INTERVAL -30 DAY)'));
    })
->groupBy('posts.id')
->orderBy('views_count', 'DESC')

With the important bit being: DB::raw('DATE_ADD(CURDATE(), INTERVAL -30 DAY)') 重要的是: DB :: raw('DATE_ADD(CURDATE(),INTERVAL -30 DAY)')

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM