2009-09-10 2 views
7

Как получить наиболее часто встречающуюся категорию для каждого тега в MySQL? В идеале я хотел бы моделировать агрегатную функцию, которая вычисляла бы mode столбца.MySQL SELECT чаще всего по группе

SELECT 
    t.tag 
    , s.category 
FROM tags t 
LEFT JOIN stuff s 
USING (id) 
ORDER BY tag; 

+------------------+----------+ 
| tag    | category | 
+------------------+----------+ 
| automotive  |  8 | 
| ba    |  8 | 
| bamboo   |  8 | 
| bamboo   |  8 | 
| bamboo   |  8 | 
| bamboo   |  8 | 
| bamboo   |  8 | 
| bamboo   |  10 | 
| bamboo   |  8 | 
| bamboo   |  9 | 
| bamboo   |  8 | 
| bamboo   |  10 | 
| bamboo   |  8 | 
| bamboo   |  9 | 
| bamboo   |  8 | 
| banana tree  |  8 | 
| banana tree  |  8 | 
| banana tree  |  8 | 
| banana tree  |  8 | 
| bath    |  9 | 
+-----------------------------+ 
+0

Просто думал, что я упомяну пару лет спустя и умнее - не организовать тег, как это, что это антипаттерны. Используйте таблицу many2many, чтобы определить взаимосвязь между тегами и элементами. Тем не менее, я все еще хочу, чтобы в MySQL была создана агрегатная функция MODE. –

ответ

3
SELECT t1.* 
FROM (SELECT tag, category, COUNT(*) AS count 
     FROM tags INNER JOIN stuff USING (id) 
     GROUP BY tag, category) t1 
LEFT OUTER JOIN 
    (SELECT tag, category, COUNT(*) AS count 
     FROM tags INNER JOIN stuff USING (id) 
     GROUP BY tag, category) t2 
    ON (t1.tag = t2.tag AND (t1.count < t2.count 
     OR t1.count = t2.count AND t1.category < t2.category)) 
WHERE t2.tag IS NULL 
ORDER BY t1.count DESC; 

Я согласен, это вид слишком много для одного запроса SQL. Любое использование GROUP BY внутри подзапроса заставляет меня вздрагивать. Вы можете сделать это выглядеть проще с помощью представлений:

CREATE VIEW count_per_category AS 
    SELECT tag, category, COUNT(*) AS count 
    FROM tags INNER JOIN stuff USING (id) 
    GROUP BY tag, category; 

SELECT t1.* 
FROM count_per_category t1 
LEFT OUTER JOIN count_per_category t2 
    ON (t1.tag = t2.tag AND (t1.count < t2.count 
     OR t1.count = t2.count AND t1.category < t2.category)) 
WHERE t2.tag IS NULL 
ORDER BY t1.count DESC; 

Но это в основном делает ту же работу за кулисами.

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

SELECT tag, category, COUNT(*) AS count 
FROM tags INNER JOIN stuff USING (id) 
GROUP BY tag, category; 

И отсортируйте полученный результат в коде приложения.

+0

У меня возникли трудности с тем, чтобы это сработало .. Казалось бы, лучше сделать агрегатную функцию MOST_FREQUENT(). Я посмотрю, находится ли это в пределах моего уровня мастерства здесь ... –

+0

К сожалению, я неправильно поняли вашу схему. Я внимательно посмотрел и издевался над тестовой базой данных, поэтому я мог быть уверен, что запрос работает. Попробуйте отредактированную версию выше. –

+0

Это похоже работа. Это немного трудно проглотить, хотя .. и есть два подвыбора, а не только один. Хотелось бы, чтобы была только встроенная агрегированная функция MEAN() или что-то: -P. Я мог бы, вероятно, написать, что с помощью C в 5 минут. –

2
SELECT tag, category 
FROM (
     SELECT @tag <> tag AS _new, 
       @tag := tag AS tag, 
       category, COUNT(*) AS cnt 
     FROM (
       SELECT @tag := '' 
       ) vars, 
       stuff 
     GROUP BY 
       tag, category 
     ORDER BY 
       tag, cnt DESC 
     ) q 
WHERE _new 

По вашим данным, это возвращает:

'automotive', 8 
'ba',   8 
'bamboo',  8 
'bananatree', 8 
'bath',  9 

Вот тестовый скрипт:

CREATE TABLE stuff (tag VARCHAR(20) NOT NULL, category INT NOT NULL); 

INSERT 
INTO stuff 
VALUES 
('automotive',8), 
('ba',8), 
('bamboo',8), 
('bamboo',8), 
('bamboo',8), 
('bamboo',8), 
('bamboo',8), 
('bamboo',10), 
('bamboo',8), 
('bamboo',9), 
('bamboo',8), 
('bamboo',10), 
('bamboo',8), 
('bamboo',9), 
('bamboo',8), 
('bananatree',8), 
('bananatree',8), 
('bananatree',8), 
('bananatree',8), 
('bath',9); 
3

(Edit: забыл DESC в ПОРЯДКА Bys)

Легко выполните с LIMIT в подзапросе. У MySQL все еще есть ограничение без LIMIT-под-подзапросов? Ниже приведен пример использования PostgreSQL.

=> select tag, (select category from stuff z where z.tag = s.tag group by tag, category order by count(*) DESC limit 1) AS category, (select count(*) from stuff z where z.tag = s.tag group by tag, category order by count(*) DESC limit 1) AS num_items from stuff s group by tag; 
    tag  | category | num_items 
------------+----------+----------- 
ba   |  8 |   1 
automotive |  8 |   1 
bananatree |  8 |   4 
bath  |  9 |   1 
bamboo  |  8 |   9 
(5 rows) 

Третья колонка необходима только в том случае, если вам нужен счет.

1

Это для простых ситуаций:

SELECT action, COUNT(action) AS ActionCount FROM log GROUP BY action ORDER BY ActionCount DESC;

 Смежные вопросы

  • Нет связанных вопросов^_^