2009-09-10 16 views
7

Jak uzyskać najczęściej występującą kategorię dla każdego znacznika w MySQL? Idealnie chciałbym symulować funkcję agregującą, która obliczyłaby kolumnę w postaci mode.MySQL SELECT najczęściej przez grupę

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

Pomyślałem, że wspomnę kilka lat później i mądrzejsi - nie organizować tagów tak, jest to antywzorzec projektowy. Użyj wielu2many tabeli, aby zdefiniować relację między tagami i przedmiotami. Mimo to, wciąż chciałbym mieć funkcję agregującą MODE w MySQL. –

Odpowiedz

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; 

zgadzam to niby za dużo dla pojedynczego zapytania SQL. Każde użycie GROUP BY wewnątrz podzapytania sprawia, że ​​wzdrygam się. Można dokonać wyglądać prostsze przy użyciu widoków:

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; 

Ale to w zasadzie tę samą pracę za kulisami.

Masz uwagi, że w kodzie aplikacji można łatwo wykonać podobną operację. Dlaczego tego nie robisz? Wykonaj prostsze kwerendy, aby uzyskać liczbę każdej kategorii:

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

I sortowania wyniku w kodzie aplikacji.

+0

Mam trudności z uzyskaniem tego do pracy. Wydaje się, że lepiej jest wykonać funkcję agregującą MOST_FREQUENT() .. Zobaczę, czy to jest w moim poziomie umiejętności tutaj ... –

+0

Przepraszam, ja źle rozumiem twój schemat. Przyjrzałem się bliżej i wyszydziłem testową bazę danych, więc mogłem być pewien, że zapytanie działa. Wypróbuj edytowaną wersję powyżej. –

+0

To wydaje się działać. Jest to trochę trudne do przełknięcia .. i są dwa sub-selekcje zamiast tylko jednego. Żałuję, że nie było wbudowanej funkcji MEAN() lub czegoś takiego: -P. Mógłbym prawdopodobnie napisać to używając C w ciągu 5 minut. –

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 

Na swoich danych, to zwraca następujące:

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

Oto skrypt testowy:

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: Zapomniałem DESC w celu bys)

Łatwy z LIMITem w podzapytaniu. Czy MySQL nadal ma ograniczenie nr-LIMIT-do-podkwerend? Poniższy przykład wykorzystuje 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) 

Trzecia kolumna jest niezbędna tylko wtedy, gdy potrzebna jest liczba.

1

To dla prostszych sytuacjach:

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