我有一个包含很多行的 MySQL 表。该表有一个流行度列。如果我按人气排序,我可以得到每个项目的排名。是否可以在不对整个表进行排序的情况下检索特定项目的排名?我不这么认为。那是对的吗?
另一种方法是创建一个新列来存储排名,对整个表进行排序,然后遍历所有行并更新排名。这是极其低效的。有没有办法在单个查询中做到这一点?
如果不先对表格进行排序或存储排名,就无法计算某事物的顺序(您称之为排名)。
但是,如果您的表被正确索引(流行度索引),数据库对其进行排序是微不足道的,因此您可以获得排名。我建议如下:
SET @rank := 0;
SELECT t.*, @rank := @rank + 1
FROM table t
ORDER BY t.popularity;
要获取具有特定“id”的项目,您可以简单地使用子查询,如下所示:
SET @rank := 0;
SELECT * FROM (
SELECT t.*, @rank := @rank + 1
FROM table t
ORDER BY t.popularity
) t2
WHERE t2.id = 1;
如果在每个读取的表上更新排名列,则第二种方法效率低下是对的。但是,根据数据库的更新次数,您可以计算每次更新的排名,并将其存储 - 这是一种缓存形式。然后,您将计算字段转换为固定值字段。
该视频介绍了 mysql 中的缓存,虽然它是特定于 rails 的,并且是一种略有不同的缓存形式,但它是一种非常相似的缓存策略。
如果您使用的是 InnoDb 表,那么您可以考虑在流行度列上构建聚集索引。(仅当按流行度排序是频繁查询时)。该决定还取决于受欢迎程度列的变化程度(0 - 3 不太好)。
您可以查看有关聚集索引的信息,看看这是否适用于您的情况:http: //msdn.microsoft.com/en-us/library/ms190639.aspx
This refers to SQL server but the concept is the same, also look up mysql documentation on this.
If you're doing this using PDO then you need to modify the query to all be within the single statement in order to get it to work properly. See PHP/PDO/MySQL: Convert Multiple Queries Into Single Query
So hobodave's answer becomes something like:
SELECT t.*, (@count := @count + 1) as rank
FROM table t
CROSS JOIN (SELECT @count := 0) CONST
ORDER BY t.popularity;
hobodave 的解决方案非常好。或者,您可以添加一个单独的排名列,然后,每当一行的流行度为UPDATE
d 时,查询以确定该流行度更新是否改变了其相对于其上方和下方行的排名,然后UPDATE
3 行受到影响。您必须分析以查看哪种方法更有效。