2013-05-06 4 views
1

Я получил эту таблицуMYSQL SUM() с GROUP BY и 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 
+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 

с использованием индекса.

+0

Я хочу, чтобы «голоса вверх» и «вниз голоса» были разделены. – pocesar

+0

@pocesar: Конечно, держите их – Quassnoi

+0

интересным, что кажется лучшим выбором. Меня раздражало идея «+» и «-_» значений столбцов, но, похоже, у меня нет выбора, и это не «сложная» вставка вроде этого медленная? – pocesar

1

Но будет ли это выполнено, если эта таблица имеет миллионы или ряды?

Нет, не будет.

Если я делаю это более простым запросом без подзапроса, то появляется временная таблица использования.

Возможно, потому, что планировщик превратил бы его в запрос, который вы отправили: ему нужно рассчитать сумму, чтобы вернуть результаты в правильном порядке.

Чтобы быстро захватить верхние голосовые вопросы, вам необходимо кэшировать результат. Добавьте поле оценки в таблицу товаров и сохраните его (например, с помощью триггеров). И проиндексируйте его. Затем вы сможете захватить 10 лучших баллов, используя сканирование индекса.

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. Является ли это эффективным или нет, зависит от количества элементов, которые у вас есть.Если каждый элемент имеет, в среднем, один или два голоса, то это может занять некоторое время. Если у вас есть фиксированный набор элементов, а всего несколько сотен или тысяч, то тогда не должно быть узким местом производительности, даже когда размер данных расширяется.

Если это резюме действительно нужно быстро, то триггер с суммарной таблицей (как объясняется в другом ответе) обеспечивает более быстрый метод поиска.

+0

Почему вы думаете, что «GROUP BY» будет сканированием индекса? – Quassnoi

+0

@Quassnoi. , , См. Это, http://dev.mysql.com/doc/refman/5.5/ru/group-by-optimization.html. –

+1

Это была моя первая попытка. используя filesort и временную таблицу ... – pocesar