[英]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.