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.