2009-08-31 8 views
1

Il y a beaucoup de questions SQL Top N sur stackoverflow mais je n'arrive pas à trouver celui qui correspond à la situation que j'ai. Je voudrais effectuer un regroupement dans une requête n supérieure. Mes données ressemblent à ceci (évidemment avec de fausses valeurs).Regroupement par un Top N dans MySQL

MY_DATE IP_ADDRESS 
1/1/09  999.999.999.999 
1/1/09  999.999.999.999 
1/1/09  999.999.999.998 
... a lot more rows 

La plage de dates de la table couvre plusieurs mois et compte plusieurs milliers de lignes par mois. Ce que je voudrais faire, c'est qu'une seule requête m'indique quelles sont les 10 adresses IP les plus fréquentes pour chaque mois. Je peux le faire pour un seul mois en utilisant les éléments suivants:

SELECT DATE_FORMAT(MY_DATE, '%b-%y') AS "MONTH", IP_ADDRESS, COUNT(*) AS HITS 
FROM MY_DATA 
WHERE DATE_FORMAT(MY_DATE, '%b-%y') = 'JAN-09' 
GROUP BY DATE_FORMAT(MY_DATE, '%b-%y'), IP_ADDRESS 
ORDER BY HITS DESC 
LIMIT 10 

Mais ce que je veux vraiment est d'être en mesure de voir le n top pour chaque mois dans l'ensemble de données. Cela m'interdit essentiellement d'utiliser la clause where que j'ai spécifiée. Bien sûr, quand je fais ça, alors je reçois juste le 10 pour tous les mois. Le résultat que je cherche devrait ressembler à ceci:

MONTH IP_ADDRESS  COUNT(*) 
JAN-09 999.999.999.999 200 
JAN-09 999.999.999.998 150 
... (8 more rows of January) 
FEB-09 999.999.999.999 320 
FEB-09 999.999.999.998 234 
... (8 more rows of February) 
MAR-09 999.999.999.999 440 
... ETC. 

Est-ce que cela peut être fait en MySQL? Il semble que la barrière que je frappe est que MySQL n'autorise pas un ORDER BY dans une requête incluse dans un UNION. Merci pour l'aide!

Répondre

3

Je viens d'essayer une requête très similaire à celui given par @Charles Bretana et il fonctionne. J'ai utilisé une vue pour aider à clarifier les choses.

CREATE TABLE my_data (
my_date DATE, 
ip_address CHAR(15) 
); 

Insérer un tas de date/paires IPADDRESS (non représenté) ...

Créer une vue pour tous les comptes par mois et l'adresse IP:

CREATE VIEW my_data_per_month as 
SELECT EXTRACT(YEAR_MONTH FROM my_date) AS month, 
    ip_address, COUNT(*) AS hits 
FROM my_data 
GROUP BY month, ip_address; 

SELECT * FROM my_data_per_month 
ORDER BY month ASC, hits DESC; 

+--------+-----------------+------+ 
| month | ip_address  | hits | 
+--------+-----------------+------+ 
| 200901 | 999.999.999.999 | 8 | 
| 200901 | 999.999.999.998 | 6 | 
| 200901 | 999.999.999.997 | 5 | 
| 200901 | 999.999.999.996 | 4 | 
| 200901 | 999.999.999.995 | 3 | 
| 200901 | 999.999.999.994 | 2 | 
| 200902 | 999.999.999.998 | 8 | 
| 200902 | 999.999.999.997 | 6 | 
| 200902 | 999.999.999.996 | 5 | 
| 200902 | 999.999.999.995 | 4 | 
| 200902 | 999.999.999.994 | 3 | 
| 200902 | 999.999.999.993 | 2 | 
| 200903 | 999.999.999.997 | 8 | 
| 200903 | 999.999.999.996 | 6 | 
| 200903 | 999.999.999.995 | 5 | 
| 200903 | 999.999.999.994 | 4 | 
| 200903 | 999.999.999.993 | 3 | 
| 200903 | 999.999.999.992 | 2 | 
+--------+-----------------+------+ 

montrer maintenant les trois premiers Adresses IP par mois:

SELECT m1.month, m1.ip_address, m1.hits 
FROM my_data_per_month m1 
LEFT OUTER JOIN my_data_per_month m2 
    ON (m1.month = m2.month AND m1.hits < m2.hits) 
GROUP BY m1.month, m1.ip_address 
HAVING COUNT(*) < 3 
ORDER BY m1.month ASC, m1.hits DESC; 

+--------+-----------------+------+ 
| month | ip_address  | hits | 
+--------+-----------------+------+ 
| 200901 | 999.999.999.999 | 8 | 
| 200901 | 999.999.999.998 | 6 | 
| 200901 | 999.999.999.997 | 5 | 
| 200902 | 999.999.999.998 | 8 | 
| 200902 | 999.999.999.997 | 6 | 
| 200902 | 999.999.999.996 | 5 | 
| 200903 | 999.999.999.997 | 8 | 
| 200903 | 999.999.999.996 | 6 | 
| 200903 | 999.999.999.995 | 5 | 
+--------+-----------------+------+ 
+0

Revenant à ce mois plus tard, j'ai enfin essayé cela et ça marche très bien! Je vous remercie! –

0

c'est une première estimation grossière, mais essayez cette

Select Month, Address 
From (Select DATE_FORMAT(MY_DATE, '%b-%y') Month, 
     IP_Address Address, Count(*) AddressCount 
     From MY_DATA 
     Group By DATE_FORMAT(MY_DATE, '%b-%y'), IP_Adress) Z 
    Join(Select DATE_FORMAT(MY_DATE, '%b-%y') Month, 
      IP_Address Address, Count(*) AddressCount 
     From MY_DATA 
     Group By DATE_FORMAT(MY_DATE, '%b-%y'), IP_Adress) ZZ 
    On ZZ.Month = Z.Month 
     And ZZ.AddressCount >= Z.AddressCount 
Group By Z.Month, Z.Address 
Where Count(ZZ.AddressCount) >= 10 
+0

Désolé, mais ce n'est même pas valide SQL. Où faites-vous la limite? –

+0

... et votre clause "ON", ou la clause where associée, est manquante, ce qui donne un produit cartésien. –

+0

@Charles: Change le 'JOIN 'à' LEFT OUTER JOIN'. Changez le 'WHERE 'de la requête externe en' HAVING'. Et changez '> =' '<=' dans cette dernière condition. –

Questions connexes