2009-08-31 3 views
1

В stackoverflow есть много вопросов по SQL Top N, но я не могу найти тот, который соответствует ситуации, которая у меня есть. Я хотел бы выполнить некоторую группировку в верхнем n запросе. Мои данные выглядят так (очевидно, с поддельными значениями).Группировка по Top N в 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 

Диапазон дат для стола охватывает несколько месяцев и имеет много тысяч строк в месяц. То, что я хотел бы сделать, это указать, что один запрос говорит мне, что 10 IP-адресов встречались чаще всего за каждый месяц. Я могу сделать это в течение одного месяца с помощью следующих действий:

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 

Но что я действительно хочу, чтобы иметь возможность видеть верхнюю п за каждый месяц в наборе данных. Это существенно запрещает мне использовать предложение where, указанное мной. Конечно, когда я это делаю, я просто получаю до 10 на все месяцы. Результат, который я ищу, должен выглядеть следующим образом:

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. 

Это можно сделать в MySQL? Кажется, что барьер, на который я нахожусь, - это то, что MySQL не разрешает ORDER BY в запросе запроса, включенном в UNION. Спасибо за помощь!

ответ

3

Я просто попытался запрос, очень похожий на тот, given по @Charles Bretana и он работает. Я использовал ПРОСМОТР, чтобы помочь прояснить ситуацию.

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

Вставьте кучу даты/пар Ipaddress (не показаны) ...

Создать представление для всех отсчетов в месяц и 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 | 
+--------+-----------------+------+ 

Теперь показывают первую тройку IP-адреса в месяц:

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

Возвращаясь к этим месяцам позже, я, наконец, пробовал это, и он работает БОЛЬШОЕ! Спасибо! –

0

это первое грубое предположение, но попробовать это

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

Извините, но это даже не верно SQL. Где вы делаете ограничение? –

+0

... и ваше предложение «ON» или связанное предложение where отсутствует, что приводит к декартовому произведению. –

+0

@Charles: измените 'JOIN' на' LEFT OUTER JOIN'. Измените 'WHERE' внешнего запроса на' HAVING'. И измените '> =' на '<=' в этом последнем условии. –

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

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