簡體   English   中英

MYSQL SUM()與GROUP BY和LIMIT

[英]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列具有任一-101 但是當這個表有數百萬或行時,這會有效嗎?

如果我在沒有子查詢的情況下使其成為更簡單的查詢,則會出現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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM