Aangezien je het schema voor results
niet hebt gegeven , ik neem aan dat het dit is of zeer vergelijkbaar (misschien extra kolommen):
create table results (
id int primary key,
user int,
foreign key (user) references <some_other_table>(id),
keyword varchar(<30>)
);
Stap 1: aggregeren op keyword/user
zoals in uw voorbeeldzoekopdracht, maar voor alle zoekwoorden:
create view user_keyword as (
select
keyword,
user,
count(*) as magnitude
from results
group by keyword, user
);
Stap 2: rangschik elke gebruiker binnen elke zoekwoordgroep (let op het gebruik van de subquery om de rijen te rangschikken):
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
);
Stap 3: selecteer alleen de rijen waar de rangorde kleiner is dan een getal:
select *
from keyword_user_ranked
where rank <= 3;
Voorbeeld:
Gebruikte basisgegevens:
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 |
+----+------+---------+
Gegroepeerd op zoekwoord en gebruiker:
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 |
+---------+------+-----------+
Gebruikers gerangschikt binnen zoekwoorden:
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 |
+---------+------+-----------+------+
Alleen top 2 van elk zoekwoord:
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 |
+---------+------+-----------+------+
Merk op dat wanneer er gelijkspel is -- zie gebruikers 2 en 4 voor trefwoord "mysql" in de voorbeelden -- alle partijen in het gelijkspel de "laatste" rang krijgen, d.w.z. als de 2e en 3e gelijk zijn, krijgen beide rang 3 toegewezen.
Prestaties:het toevoegen van een index aan de trefwoorden en gebruikerskolommen zal helpen. Ik heb een tabel die op een vergelijkbare manier wordt opgevraagd met 4000 en 1300 verschillende waarden voor de twee kolommen (in een tabel met 600000 rijen). U kunt de index als volgt toevoegen:
alter table results add index keyword_user (keyword, user);
In mijn geval daalde de zoektijd van ongeveer 6 seconden naar ongeveer 2 seconden.