执行以下MySQL语句的更有效方法是什么?我正在努力使我的查询尽可能高效。在此先感谢!!
MySQL查询:
SELECT nick as viewer,
CONVERT(rank, UNSIGNED) as rank,
CONVERT(FLOOR(amount), UNSIGNED) as amount
FROM
(SELECT @rank:=@rank+1 AS rank,
nick, amount FROM points,
(SELECT @rank := 0) t
ORDER BY amount DESC) as t
LIMIT 50;
解释输出:
mysql> EXPLAIN SELECT nick as viewer, CONVERT(rank, UNSIGNED) as rank, CONVERT(FLOOR(amount), UNSIGNED) as amount FROM (SELECT @rank:=@rank+1 AS rank, nick, amount FROM points, (SELECT @rank := 0) t ORDER BY amount DESC) as t LIMIT 50;
+----+-------------+------------+--------+---------------+------+---------+------+--------+----------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+------------+--------+---------------+------+---------+------+--------+----------------+
| 1 | PRIMARY | <derived2> | ALL | NULL | NULL | NULL | NULL | 102298 | |
| 2 | DERIVED | <derived3> | system | NULL | NULL | NULL | NULL | 1 | Using filesort |
| 2 | DERIVED | points | ALL | NULL | NULL | NULL | NULL | 103890 | |
| 3 | DERIVED | NULL | NULL | NULL | NULL | NULL | NULL | NULL | No tables used |
+----+-------------+------------+--------+---------------+------+---------+------+--------+----------------+
4 rows in set (3.01 sec)
表格描述:
mysql> describe points;
+--------+---------------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+--------+---------------+------+-----+---------+-------+
| nick | varchar(255) | NO | PRI | NULL | |
| amount | decimal(10,4) | NO | | NULL | |
+--------+---------------+------+-----+---------+-------+
2 rows in set (0.09 sec)
示例输出:
+--------------------------+------+--------+
| viewer | rank | amount |
+--------------------------+------+--------+
| dakuda | 1 | 768 |
| resurrex | 2 | 575 |
| stgchrist | 3 | 502 |
| artisun001 | 4 | 446 |
| xjenniewasherex | 5 | 366 |
....
50 rows in set (3.23 sec)
答案 0 :(得分:2)
这应该稍快一些。我尝试用2 ^ 17个随机记录(以及你的样本)填写你的表格来尝试它:
SELECT nick AS viewer, @counter := @counter + 1 AS rank, FLOOR(amount) AS amount
FROM points
JOIN (SELECT @counter := 0) AS init
ORDER BY amount DESC
LIMIT 50
得到了,
+----------------------------------+------+--------+
| viewer | rank | amount |
+----------------------------------+------+--------+
| dakuda | 1 | 768 |
| 9c91a2e708f61d7d790c00e289509893 | 2 | 699 |
| 114372e32506acb21a389463f761526c | 3 | 699 |
| 522f93c197550a532727ae894b164743 | 4 | 699 |
| 55f272eecfaff0dbd3d55a2b525d772f | 5 | 699 |
...
| 5f5c9b1dd560b37307eff23fe1b3e58f | 50 | 699 |
+----------------------------------+------+--------+
50 rows in set (0.02 sec)
区别在于排名方法 - 您实际上是从points
中选择所有行,然后 LIMIT
在外部查询中。您也可以像这样修改您的版本 - 将LIMIT
置于其中 -
SELECT nick as viewer, CONVERT(rank, UNSIGNED) as rank, CONVERT(FLOOR(amount),
UNSIGNED) as amount FROM (SELECT @rank:=@rank+1 AS rank,
nick, amount FROM points, (SELECT @rank := 0) t
ORDER BY amount DESC LIMIT 50) as t;
并获得显着的性能提升。
如果您必须重复显示此信息,并且您可以估算任何给定分数在两者之间可能经历的最大差异,那么您可以通过保存最后一个最低分来显着提高绩效
假设没有一个玩家每天可以获得超过20分,并且前50名中显示的最后一个最低分数是500,这是三天前。
如果确实如此,那可能发生的最糟糕的事情就是前五十名中没有一名球员获得单点而其他一些球员每天得分为20分,达到60分。 但如果他们低于440,他们仍然没有机会进入前五十,所以你可以在INDEX
和amount
上添加WHERE
可以刮掉大部分无需检查的数据的条件:
CREATE INDEX points_amount_ndx ON points(amount);
SELECT
nick AS viewer,
@counter := @counter + 1 AS rank,
FLOOR(amount) AS amount
FROM points, (SELECT @counter := 0) AS init
WHERE amount > 440
ORDER BY amount DESC
LIMIT 50
然后在下一次保存金额的第五十个值和当前时间戳。
对于受到重创的网站,如果点数更新之间出现更多查询的可能性很大,您还可以使用SQL_CACHE
:
SELECT SQL_CACHE
nick AS viewer,
@counter := @counter + 1 AS rank,
FLOOR(amount) AS amount
FROM points, (SELECT @counter := 0) AS init
WHERE amount > 440
ORDER BY amount DESC
LIMIT 50
...这会使我的查询时间减少到有效为零。每次更新点后,缓存都会失效,并重新计算查询... 一次。