Mysql
 sql >> Base de données >  >> RDS >> Mysql

Top 'n' résultats pour chaque mot-clé

Puisque vous n'avez pas donné le schéma pour les results , je suppose que c'est ceci ou très similaire (peut-être des colonnes supplémentaires) :

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

Étape 1 : agrégé par keyword/user comme dans votre exemple de requête, mais pour tous les mots clés :

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

Étape 2 : classez chaque utilisateur dans chaque groupe de mots clés (notez l'utilisation de la sous-requête pour classer les lignes) :

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

Étape 3 : sélectionnez uniquement les lignes dont le rang est inférieur à un certain nombre :

select * 
from keyword_user_ranked 
where rank <= 3;

Exemple :

Données de base utilisées :

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

Regroupés par mot-clé et utilisateur :

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

Utilisateurs classés par mots-clés :

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

Uniquement les 2 premiers de chaque mot-clé :

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

Notez que lorsqu'il y a des égalités - voir les utilisateurs 2 et 4 pour le mot-clé "mysql" dans les exemples - toutes les parties à égalité obtiennent le "dernier" rang, c'est-à-dire que si le 2ème et le 3ème sont à égalité, les deux se voient attribuer le rang 3.

Performance :l'ajout d'un index aux colonnes de mots clés et d'utilisateurs sera utile. J'ai une table interrogée de la même manière avec 4000 et 1300 valeurs distinctes pour les deux colonnes (dans une table de 600000 lignes). Vous pouvez ajouter l'index comme ceci :

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

Dans mon cas, le temps de requête est passé d'environ 6 secondes à environ 2 secondes.