我有这张桌子
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
答案 0 :(得分:2)
不,这对于数百万行来说效率不高。
您必须创建一个支持聚合表,用于存储每个项目的投票数:
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
有效使用索引。
答案 1 :(得分:1)
但是当这个表有数百万或几行时,这会有效吗?
不,它不会。
如果我在没有子查询的情况下使其成为更简单的查询,则会出现使用临时表。
可能是因为计划程序会将其转换为您发布的查询:它需要计算总和以便以正确的顺序返回结果。
要快速获取最高投票问题,您需要缓存结果。在项目表中添加分数字段并进行维护(例如使用触发器)。索引它。然后,您将能够使用索引扫描获取前10个分数。
答案 2 :(得分:0)
首先,您不需要子查询,因此您可以将查询重写为:
SELECT `item_id`, SUM(vote) AS `sum`
FROM `votes`
GROUP BY `item_id`
ORDER BY `a`.`sum` DESC
LIMIT 10
其次,您可以在votes(item_id, vote)
上构建索引。然后group by
将成为索引扫描。随着表变大,将花费时间,但对于合理的数据大小,它应该是可管理的。
最后,通过这种查询结构,您需要对最终order by
进行文件排序。这是否有效取决于您拥有的物品数量。如果每个项目平均有一到两票,那么这可能需要一些时间。如果你有一组固定的项目而且只有几百或几千,那么即使数据大小扩大,也不应该成为性能瓶颈。
如果这个摘要确实是你需要的,那么带有摘要表的触发器(如另一个答案中所述)提供了一种更快的检索方法。