Mysql
 sql >> Base de Dados >  >> RDS >> Mysql

Principais 'n' resultados para cada palavra-chave


Como você não forneceu o esquema para results , vou assumir que é isso ou muito semelhante (talvez colunas extras):
create table results (
  id int primary key,
  user int,
    foreign key (user) references <some_other_table>(id),
  keyword varchar(<30>)
);

Etapa 1: agregado por keyword/user como em sua consulta de exemplo, mas para todas as palavras-chave:
create view user_keyword as (
  select
    keyword,
    user,
    count(*) as magnitude
  from results
  group by keyword, user
);

Etapa 2: classifique cada usuário em cada grupo de palavras-chave (observe o uso da subconsulta para classificar as linhas):
create view keyword_user_ranked as (
  select 
    keyword,
    user,
    magnitude,
    (select count(*) 
     from user_keyword 
     where l.keyword = keyword and magnitude >= l.magnitude
    ) as rank
  from
    user_keyword l
);

Etapa 3: selecione apenas as linhas em que a classificação é menor que algum número:
select * 
from keyword_user_ranked 
where rank <= 3;

Exemplo:

Dados básicos usados:
mysql> select * from results;
+----+------+---------+
| id | user | keyword |
+----+------+---------+
|  1 |    1 | mysql   |
|  2 |    1 | mysql   |
|  3 |    2 | mysql   |
|  4 |    1 | query   |
|  5 |    2 | query   |
|  6 |    2 | query   |
|  7 |    2 | query   |
|  8 |    1 | table   |
|  9 |    2 | table   |
| 10 |    1 | table   |
| 11 |    3 | table   |
| 12 |    3 | mysql   |
| 13 |    3 | query   |
| 14 |    2 | mysql   |
| 15 |    1 | mysql   |
| 16 |    1 | mysql   |
| 17 |    3 | query   |
| 18 |    4 | mysql   |
| 19 |    4 | mysql   |
| 20 |    5 | mysql   |
+----+------+---------+

Agrupados por palavra-chave e usuário:
mysql> select * from user_keyword order by keyword, magnitude desc;
+---------+------+-----------+
| keyword | user | magnitude |
+---------+------+-----------+
| mysql   |    1 |         4 |
| mysql   |    2 |         2 |
| mysql   |    4 |         2 |
| mysql   |    3 |         1 |
| mysql   |    5 |         1 |
| query   |    2 |         3 |
| query   |    3 |         2 |
| query   |    1 |         1 |
| table   |    1 |         2 |
| table   |    2 |         1 |
| table   |    3 |         1 |
+---------+------+-----------+

Usuários classificados em palavras-chave:
mysql> select * from keyword_user_ranked order by keyword, rank asc;
+---------+------+-----------+------+
| keyword | user | magnitude | rank |
+---------+------+-----------+------+
| mysql   |    1 |         4 |    1 |
| mysql   |    2 |         2 |    3 |
| mysql   |    4 |         2 |    3 |
| mysql   |    3 |         1 |    5 |
| mysql   |    5 |         1 |    5 |
| query   |    2 |         3 |    1 |
| query   |    3 |         2 |    2 |
| query   |    1 |         1 |    3 |
| table   |    1 |         2 |    1 |
| table   |    3 |         1 |    3 |
| table   |    2 |         1 |    3 |
+---------+------+-----------+------+

Apenas os 2 primeiros de cada palavra-chave:
mysql> select * from keyword_user_ranked where rank <= 2 order by keyword, rank asc;
+---------+------+-----------+------+
| keyword | user | magnitude | rank |
+---------+------+-----------+------+
| mysql   |    1 |         4 |    1 |
| query   |    2 |         3 |    1 |
| query   |    3 |         2 |    2 |
| table   |    1 |         2 |    1 |
+---------+------+-----------+------+

Observe que quando há empates - veja os usuários 2 e 4 para a palavra-chave "mysql" nos exemplos - todas as partes empatadas obtêm a classificação "última", ou seja, se o 2º e o 3º estiverem empatados, ambos receberão o rank 3.

Desempenho:adicionar um índice às colunas de palavra-chave e usuário ajudará. Eu tenho uma tabela sendo consultada de maneira semelhante com 4.000 e 1.300 valores distintos para as duas colunas (em uma tabela de 600.000 linhas). Você pode adicionar o índice assim:
alter table results add index keyword_user (keyword, user);

No meu caso, o tempo de consulta caiu de cerca de 6 segundos para cerca de 2 segundos.