【发布时间】:2011-01-12 19:23:39
【问题描述】:
我有一个包含很多行的 MySQL 表。该表有一个流行度列。如果我按人气排序,我可以得到每个项目的排名。是否可以在不对整个表进行排序的情况下检索特定项目的排名?我不这么认为。对吗?
另一种方法是创建一个新列来存储排名,对整个表进行排序,然后遍历所有行并更新排名。这是极其低效的。有没有办法在单个查询中做到这一点?
【问题讨论】:
我有一个包含很多行的 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;
【讨论】:
如果在每次读取的表上更新排名列,则第二种方法效率低下是对的。但是,根据数据库的更新次数,您可以计算每次更新的排名,并将其存储 - 这是一种缓存形式。然后,您将计算字段转换为固定值字段。
This video 涵盖了 mysql 中的缓存,虽然它是特定于 rails 的,并且是一种略有不同的缓存形式,但它是一种非常相似的缓存策略。
【讨论】:
如果您使用 InnoDb 表,那么您可以考虑在流行度列上构建聚集索引。 (仅当按流行度排序是频繁查询时)。该决定还取决于受欢迎程度列的变化程度(0 - 3 不太好)。
您可以查看有关聚集索引的信息,看看这是否适用于您的情况:http://msdn.microsoft.com/en-us/library/ms190639.aspx
这是指SQL server,但概念是一样的,也可以查看mysql文档。
【讨论】:
如果您使用 PDO 执行此操作,则需要将查询修改为全部包含在单个语句中,以使其正常工作。见PHP/PDO/MySQL: Convert Multiple Queries Into Single Query
所以 hobodave 的回答变成了这样:
SELECT t.*, (@count := @count + 1) as rank
FROM table t
CROSS JOIN (SELECT @count := 0) CONST
ORDER BY t.popularity;
【讨论】:
hobodave 的解决方案非常好。或者,您可以添加一个单独的排名列,然后,只要一行的受欢迎程度为UPDATEd,查询以确定该受欢迎程度更新是否改变了其相对于其上方和下方行的排名,然后UPDATE 受影响的 3 行。您必须进行剖析以查看哪种方法更有效。
【讨论】: