MYSQL SUM(),具有分组依据和限制
我有这张桌子 创建表“投票” `item_id`int10未签名非空, `用户_id`int10未签名非空, `投票'tinyint4非空默认值'0', 主键`item\u id`,`user\u id`, 键'FK\u vote\u user``user\u id`, 键'vote``vote`, KEY`item``item\u id`, 更新级联上的约束'FK\u vote\u item'外键'item\u id'引用'items''id', 更新级联上的约束`FK\u vote\u user`外键`user\u id`引用`users``id` ENGINE=InnoDB默认字符集=utf8 COLLATE=utf8\U unicode\U ci 我得到了这个简单的选择 选择 `a`.`item_id`、`a`.`sum` 从…起 选择 `项目id`,投票结果为'sum` 从…起 `投票` 按“item_id”分组作为 按a排序。`sum`DESC 限制10 现在,只有250行,没有问题,但它使用的是文件排序。投票列有-1、0或1。但是,当这个表有数百万行或更多行时,这会被执行吗 如果我使它成为一个没有子查询的更简单的查询,那么将显示using临时表 Explain在0.00170秒内完成查询:MYSQL SUM(),具有分组依据和限制,mysql,group-by,sum,sql-order-by,sql-limit,Mysql,Group By,Sum,Sql Order By,Sql Limit,我有这张桌子 创建表“投票” `item_id`int10未签名非空, `用户_id`int10未签名非空, `投票'tinyint4非空默认值'0', 主键`item\u id`,`user\u id`, 键'FK\u vote\u user``user\u id`, 键'vote``vote`, KEY`item``item\u id`, 更新级联上的约束'FK\u vote\u item'外键'item\u id'引用'items''id', 更新级联上的约束`FK\u vote\u us
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
有效地使用索引
但是,当这个表有数百万行或更多行时,这会被执行吗
不,不会的
如果我使它成为一个没有子查询的更简单的查询,那么将显示using临时表
可能是因为计划者会将其转换为您发布的查询:它需要计算总和以正确的顺序返回结果
要快速抓取投票最多的问题,您需要缓存结果。在项目表中添加分数字段,并使用触发器等方式进行维护。并将其索引。然后,您可以使用索引扫描获取前10名分数。首先,您不需要子查询,因此可以将查询重写为:
SELECT `item_id`, SUM(vote) AS `sum`
FROM `votes`
GROUP BY `item_id`
ORDER BY `a`.`sum` DESC
LIMIT 10
第二,你可以在votesitem_id,vote上建立一个索引。然后将对分组依据进行索引扫描。随着表变得越来越大,这将需要时间,但对于合理的数据大小,它应该是可管理的
最后,使用这种查询结构,您需要对最终order by进行文件排序。这是否有效取决于您拥有的物品数量。如果每个项目平均有一两票,那么这可能需要一些时间。如果您有一组固定的项,但只有几百或几千项,那么即使数据大小在扩大,也不应该成为性能瓶颈
如果此摘要确实是您快速需要的内容,那么另一个答案中解释的带有摘要表的触发器提供了一种更快的检索方法。如果使用filesort,则几乎肯定不会执行。我现在遇到了一个关于大型数据集和文件排序的问题。不漂亮。@pocesar:当然,一定要让它们有趣,这似乎是最好的选择。我对+'ing和-'ing列值的想法很恼火,但似乎我别无选择,像这样复杂的插入不是很慢吗?@pocesar:这种插入有什么复杂之处?使用变量而不是常量我使用准备好的语句,在重复键上, etc@pocesar:上面这些都不复杂。为什么你认为分组方式是索引扫描?@Quassnoi。看看这个,。@GordonLinoff:我已经读过了,不过:投票id的索引扫描怎么会更有效?@pocesar。索引必须是item_id,vote,按顺序排列。@pocesar:无论发生什么情况,都将有filesort和temporary。虽然MAX和MIN确实可以,但SUM不能以这种方式优化。查询必须访问所有记录,将所有投票汇总,并对它们进行排序,以找到前十名。