Mysql
 sql >> Database >  >> RDS >> Mysql

Primi 'n' risultati per ogni parola chiave

Poiché non hai fornito lo schema per i results , suppongo che sia questo o molto simile (forse colonne extra):

create table results (
  id int primary key,
  user int,
    foreign key (user) references <some_other_table>(id),
  keyword varchar(<30>)
);

Passaggio 1: aggrega per keyword/user come nella tua query di esempio, ma per tutte le parole chiave:

create view user_keyword as (
  select
    keyword,
    user,
    count(*) as magnitude
  from results
  group by keyword, user
);

Passaggio 2: classifica ogni utente all'interno di ogni gruppo di parole chiave (nota l'uso della sottoquery per classificare le righe):

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
);

Passaggio 3: seleziona solo le righe in cui il rango è inferiore a un numero:

select * 
from keyword_user_ranked 
where rank <= 3;

Esempio:

Dati di base utilizzati:

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

Raggruppati per parola chiave e utente:

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

Utenti classificati all'interno di parole chiave:

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

Solo i primi 2 di ogni parola chiave:

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

Nota che quando ci sono pareggi -- vedi gli utenti 2 e 4 per la parola chiave "mysql" negli esempi -- tutte le parti in parità ottengono il rango "ultimo", cioè se il 2° e il 3° sono in parità, a entrambi viene assegnato il rango 3.

Rendimento:l'aggiunta di un indice alla parola chiave e alle colonne utente aiuterà. Ho una tabella sottoposta a query in modo simile con 4000 e 1300 valori distinti per le due colonne (in una tabella di 600000 righe). Puoi aggiungere l'indice in questo modo:

alter table results add index keyword_user (keyword, user);

Nel mio caso, il tempo di query è sceso da circa 6 secondi a circa 2 secondi.