Eloquent/SQL - 获取查询的行号(排名)

Posted

技术标签:

【中文标题】Eloquent/SQL - 获取查询的行号(排名)【英文标题】:Eloquent/SQL - Get the row number of a query (rank) 【发布时间】:2019-03-26 17:35:32 【问题描述】:

我想知道基于我的数据库结构的排名:

我有一个模型 Post,它属于一个名为 Edition 的模型(也有一个 Edition 有很多 Post)。

一个Post 有多个Like

我想根据特定Edition 中的Like 计数了解Post 的排名。

代码:

// Define the id of an edition
$edition_id = 53;
// The id of the post to retrieve rank
$post_id = 132;
// Compose the query by select all posts, of interested edition ...
$query = App\Models\Post::where('edition_id', $edition_id)
    // ... with like count (this produce an additional field named likes_count) ...
    ->withCount('likes')
    // ... in descendig order by this count.
    ->orderBy('likes_count', 'desc');
// By execute it I can get right results.
$query->get();

对于Laravel Eloquent ORM不熟悉的人,我报告一下上面代码执行的sql查询:

select `posts`.*, (select count(*) from `likes` where `posts`.`id` = `likes`.`post_id` and `likes`.`deleted_at` is null) as `likes_count`
from `posts`
where `edition_id` = '53' and `posts`.`deleted_at` is null
order by `likes_count` desc

我报告查询结果:

=> Illuminate\Database\Eloquent\Collection #994
all: [
    App\Models\Post #993
        id: 135,
        likes_count: 4,
    ,
    App\Models\Post #1075
        id: 134,
        likes_count: 3,
    ,
    App\Models\Post #1091
        id: 133,
        likes_count: 2,
    ,
    App\Models\Post #997
        id: 132,
        likes_count: 1,
    ,
    App\Models\Post #1038
        id: 131,
        likes_count: 0,
    ,
],

如何从组合查询的结果中获取具有某个id的记录的行位置?

例如,如何检索id = 132记录的排名结果?

【问题讨论】:

你用的是什么版本的 mysql @JonasStaudenmeir:在我的开发环境中,我有mysql Ver 14.14 Distrib 5.7.19, for macos10.12 (x86_64) using EditLine wrapper 你见过***.com/q/3333665/4848587吗? @JonasStaudenmeir 感谢您的回复。是的,我已经看过它并且我读到了ROW_NUMBER(),但我无法以正确的方式实现它。我使用正确的行号进行查询,但如果我按 id 添加过滤器,结果始终为 1,而不是 where id = X 过滤器之前的排名数。 何时检索单个帖子? 【参考方案1】:

您可以使用子查询:

$query = Post::where('edition_id', $edition_id)
    ->withCount('likes')
    ->selectRaw('@rank := @rank + 1 rank')
    ->from(DB::raw('`posts`, (SELECT @rank := 0) vars'))
    ->orderBy('likes_count', 'desc');

$posts = Post::fromSub($query, 'posts')->paginate();

$post = Post::fromSub($query, 'posts')->find($post_id);

Laravel

Builder::macro('fromSub', function($query, $as) 
    $this->bindings = array_merge(
        array_slice($this->bindings, 0, 1),
        ['from' => $query->getBindings()],
        array_slice($this->bindings, 1)
    );

    $sql = '('.$query->toSql().') as '.$this->grammar->wrap($as);

    return $this->from(DB::raw($sql));
);

【讨论】:

感谢您的回复!我试过了,但我的 Laravel 版本是最后一个 LTS 版本5.5.34。方法fromRaw fromSub 可从版本5.6 获得:还有其他选择吗?如果不是,我会阅读升级指南,但我想继续使用 LTS 版本。 我已替换 fromRaw() 并为 Laravel 5.5 添加了解决方法。您可以使用宏实现fromSub() 嗨 Jonas,对不起,我正在计划将 laravel 更新到 5.6,以避免使用您发布的宏解决方案给搜索引擎带来复杂性。没试过,不过明天我会部署当前版本,试试子查询的方法。您知道使用这些查询是否存在任何性能问题? 不,查询应该相当快。 嗨乔纳斯,抱歉迟到了,我很忙。昨天我已将 laravel 更新到 5.6.X 版本,并尝试实施您的解决方案。效果很好!非常感谢您的出色帮助。【参考方案2】:

您可以使用search()方法找到密钥:

搜索方法在集合中搜索给定的值,如果找到则返回其键。如果未找到该项目,则返回 false。

$id = 132;

$key = $query->search(function($post,$id) 
    return $post->id === $id;
);

【讨论】:

嗨卢卡斯,感谢您的回复!正如您所说,search 方法搜索 collection。这意味着两件事:1)在我的查询构建器上产生一个Call to undefined method Illuminate\Database\Query\Builder::search():要开始工作,我必须执行它并将结果放入带有->get()method 的集合中。 2)通过执行意味着我必须迭代集合结果中的每个对象:如果我在版本中有成百上千的帖子,它猜测它在时间和内存使用方面的表现会很差。 是的,你是对的,你必须打电话给get 让它工作,可能不会是最优化的事情,但我希望你的情况是我能想到的唯一答案它以任何方式帮助你:)

以上是关于Eloquent/SQL - 获取查询的行号(排名)的主要内容,如果未能解决你的问题,请参考以下文章

C ++匹配文件中的字符串并获取行号

mysql计算排名

对记录进行排序后使用密集排名

使用百分比选择(LIMIT X,1)获取行号的最快方法?

SQL 查询获取主选择查询上的行排名或位置

Logback输出行号