Группировка по N в MySQL - PullRequest
       22

Группировка по N в MySQL

1 голос
/ 31 августа 2009

Существует много вопросов по SQL Top N по стеку, но я не могу найти тот, который соответствует моей ситуации. Я хотел бы выполнить некоторую группировку в верхнем n запросе. Мои данные выглядят так (очевидно, с поддельными значениями).

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

Но что я действительно хочу, так это иметь возможность видеть верхние n за каждый месяц в наборе данных. По сути, это запрещает мне использовать оговорку 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. Спасибо за помощь!

Ответы [ 2 ]

3 голосов
/ 01 сентября 2009

Я только что попробовал запрос, очень похожий на запрос , данный @Charles Bretana, и он работает. Я использовал ВИД, чтобы прояснить ситуацию.

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

Вставить несколько пар дата / IP-адрес (не показаны) ...

Создание представления для всех подсчетов в месяц и 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 голосов
/ 31 августа 2009

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

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
...