MySQL: запросите верхние n агрегаций - PullRequest
1 голос
/ 27 мая 2009

У меня есть таблица, в которой подсчитываются случаи появления одного конкретного действия разными пользователями над разными объектами:

CREATE TABLE `Actions` (
    `object_id` int(10) unsigned NOT NULL,
    `user_id` int(10) unsigned NOT NULL,
    `actionTime` datetime
);

Каждый раз, когда пользователь выполняет это действие, вставляется строка. Я могу посчитать, сколько действий было выполнено над каждым объектом, и упорядочить объекты по «активности»:

SELECT object_id, count(object_id) AS action_count 
FROM `Actions` 
GROUP BY object_id 
ORDER BY action_count;

Как я могу ограничить результаты для первых n объектов? Предложение LIMIT применяется перед агрегированием, поэтому оно приводит к неверным результатам. Таблица потенциально огромна (миллионы строк), и мне, вероятно, нужно считать десятки раз в минуту, поэтому я хотел бы сделать это максимально эффективно.

edit : На самом деле машина права, и я ошибся со временем применения LIMIT. Мой запрос дал правильные результаты, но графический интерфейс пользователя, представивший их мне, отбросил меня ... этот вид делает этот вопрос бессмысленным. Извините!

Ответы [ 4 ]

2 голосов
/ 27 мая 2009

На самом деле ... LIMIT применяется последним, после возможного предложения HAVING. Так что это не должно давать вам неправильные результаты. Однако, поскольку LIMIT применяется последним, он не обеспечит более быстрое выполнение вашего запроса, поскольку временную таблицу необходимо будет создать и отсортировать в порядке количества действий, прежде чем отбрасывать результат. Также не забудьте отсортировать по убыванию:

SELECT object_id, count(object_id) AS action_count 
FROM `Actions` 
GROUP BY object_id 
ORDER BY action_count DESC
LIMIT 10;

Вы можете попробовать добавить индекс для object_id для оптимизации. Таким образом, будет сканироваться только индекс вместо таблицы Actions .

1 голос
/ 03 марта 2011

Я знаю, что этой ветке 2 года, но stackflow все еще находит ее актуальной, так что вот мои $ 0,02. Предложения ORDER BY вычислительно очень дороги, поэтому их следует избегать в больших таблицах. Уловка, которую я использовал (частично из SQL для Smarties Джо Селко), выглядит примерно так:

SELECT COUNT(*) AS counter, t0.object_id FROM (SELECT COUNT(*), actions.object_id FROM actions GROUP BY id) AS t0, (SELECT COUNT(*), actions.object_id FROM actions GROUP BY id) AS t1 WHERE t0.object_id < t1.object_id  GROUP BY object_id HAVING counter < 15

Даст вам 15 лучших отредактированных объектов без сортировки. Обратите внимание, что начиная с версии v5, mysql будет кэшировать наборы результатов только для точно дублированных (включая пробельные символы) запросов, поэтому вложенный запрос не будет кэшироваться. Использование представления решило бы эту проблему.

Да, это три запроса вместо двух, и единственным преимуществом является отсутствие необходимости сортировать сгруппированный запрос, но если у вас много групп, это будет быстрее.

Примечание: запрос действительно полезен для медианных функций без сортировки

1 голос
/ 27 мая 2009

Как насчет:

SELECT * FROM
(
SELECT object_id, count(object_id) AS action_count 
FROM `Actions` 
GROUP BY object_id 
ORDER BY action_count
)
LIMIT 15

Кроме того, если у вас есть некоторая мера того, какое минимальное количество действий должно быть включено (например, число первых n наверняка превышает 1000), вы можете повысить эффективность, добавив предложение HAVING:

SELECT * FROM
(
SELECT object_id, count(object_id) AS action_count 
FROM `Actions` 
GROUP BY object_id 
HAVING action_count > 1000
ORDER BY action_count
)
LIMIT 15
0 голосов
/ 27 мая 2009
SELECT * FROM (SELECT object_id, count(object_id) AS action_count 
        FROM `Actions` 
        GROUP BY object_id 
        ORDER BY action_count) LIMIT 10;
...