[英]MYSQL SUM() with GROUP BY and LIMIT
我拿到了这张桌子
CREATE TABLE `votes` (
`item_id` int(10) unsigned NOT NULL,
`user_id` int(10) unsigned NOT NULL,
`vote` tinyint(4) NOT NULL DEFAULT '0',
PRIMARY KEY (`item_id`,`user_id`),
KEY `FK_vote_user` (`user_id`),
KEY `vote` (`vote`),
KEY `item` (`item_id`),
CONSTRAINT `FK_vote_item` FOREIGN KEY (`item_id`) REFERENCES `items` (`id`) ON UPDATE CASCADE,
CONSTRAINT `FK_vote_user` FOREIGN KEY (`user_id`) REFERENCES `users` (`id`) ON UPDATE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci
我得到了这个简单的选择
SELECT
`a`.`item_id`, `a`.`sum`
FROM
(SELECT
`item_id`, SUM(vote) AS `sum`
FROM
`votes`
GROUP BY `item_id`) AS a
ORDER BY `a`.`sum` DESC
LIMIT 10
现在,只有250行,没有问题,但它正在使用filesort。 该vote
列具有任一-1
, 0
或1
。 但是当这个表有数百万或行时,这会有效吗?
如果我在没有子查询的情况下使其成为更简单的查询,则会出现using temporary table
。
解释给出(查询在0.00170s完成):
id select_type table type possible_keys key key_len ref rows Extra
1 PRIMARY <derived2> ALL NULL NULL NULL NULL 33 Using filesort
2 DERIVED votes index NULL PRIMARY 8 NULL 250
不,这对于数百万行来说效率不高。
您必须创建一个支持聚合表,该表将存储每个项目的投票:
CREATE TABLE item_votes
(
item_id INT NOT NULL PRIMARY KEY,
votes UNSIGNED INT NOT NULL,
upvotes UNSIGNED INT NOT NULL,
downvotes UNSIGNED INT NOT NULL,
KEY (votes),
KEY (upvotes),
KEY (downvotes)
)
并在每次投票时更新它:
INSERT
INTO item_votes (item_id, votes, upvotes, downvotes)
VALUES (
$item_id,
CASE WHEN $upvote THEN 1 ELSE -1 END,
CASE WHEN $upvote THEN 1 ELSE 0 END,
CASE WHEN $upvote THEN 0 ELSE 1 END
)
ON DUPLICATE KEY
UPDATE
SET votes = votes + VALUES(upvotes) - VALUES(downvotes),
upvotes = upvotes + VALUES(upvotes),
downvotes = downvotes + VALUES(downvotes)
然后选择前10票:
SELECT *
FROM item_votes
ORDER BY
votes DESC, item_id DESC
LIMIT 10
有效地使用索引。
但是当这个表有数百万或行时,这会有效吗?
不,它不会。
如果我在没有子查询的情况下使其成为更简单的查询,则会出现使用临时表。
可能是因为计划程序会将其转换为您发布的查询:它需要计算总和以按正确的顺序返回结果。
要快速获取最高投票问题,您需要缓存结果。 在项目表中添加分数字段并进行维护(例如使用触发器)。 索引它。 然后,您将能够使用索引扫描获取前10个分数。
首先,您不需要子查询,因此您可以将查询重写为:
SELECT `item_id`, SUM(vote) AS `sum`
FROM `votes`
GROUP BY `item_id`
ORDER BY `a`.`sum` DESC
LIMIT 10
其次,您可以建立votes(item_id, vote)
索引votes(item_id, vote)
。 然后, group by
将成为索引扫描。 随着表变大,这将花费时间,但对于合理的数据大小,它应该是可管理的。
最后,使用此查询结构,您需要对最终order by
执行文件排序。 这是否有效取决于您拥有的物品数量。 如果每个项目平均有一到两票,那么这可能需要一些时间。 如果你有一组固定的项目并且只有几百或几千,那么即使数据大小扩大,也不应该成为性能瓶颈。
如果这个摘要确实是您需要的,那么带有摘要表的触发器(如另一个答案中所述)提供了更快的检索方法。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.