Les réponses de Shlomi et de Zishan (qui utilise le code de Shlomi) ne donnent absolument pas de résultats précis, comme je l'ai découvert en examinant les résultats d'un de mes grands tableaux. Comme répondu ailleurs, il est apparemment impossible de calculer les rangs centiles dans une seule requête MySQL : Rang percentile SQL
L'approche de Shlomi Noach, qui utilise des variables définies par l'utilisateur, est la suivante - à première vue comme si cela fonctionnait bien pour les deux premiers pourcentages de classement, mais cela dégénère rapidement pour les rangs inférieurs de votre tableau. Regardez les résultats de vos données par vous-même, comme je l'ai fait.
Voir ce billet de Roland Bouman qui explique pourquoi l'approche de Shlomi utilisant des variables définies par l'utilisateur dans une seule instruction SQL ne fonctionne pas, avec une proposition de meilleure solution :
http://rpbouman.blogspot.com/2009/09/mysql-another-ranking-trick.html
J'ai donc adapté le code de Bouman à cette fin et voici ma solution, qui combine nécessairement PHP et MySQL :
Étape 1) Calculez et stockez le rang absolu pour chaque ligne en soumettant les deux requêtes suivantes :
SET @@group_concat_max_len := @@max_allowed_packet;
UPDATE mytable INNER JOIN (SELECT ID, FIND_IN_SET(
score,
(SELECT GROUP_CONCAT(
DISTINCT score
ORDER BY score DESC
)
FROM mytable)
) AS rank
FROM mytable) AS a
ON mytable.ID=a.ID
SET mytable.rank = rank;
Étape 2 : Récupérer le nombre total de lignes (et stocker le résultat dans une variable PHP $total)
SELECT COUNT(ID) FROM mytable
Étape 3 : Utilisez une boucle PHP pour itérer dans le tableau et utiliser le rang absolu de chaque ligne pour calculer le rang centile de la ligne :
3a) Boucle à travers :
SELECT ID, rank FROM mytable
tout en stockant ces valeurs de ligne comme $ID et $rank en PHP
3b) Pour chaque ligne exécutée :
$sql = 'UPDATE mytable INNER JOIN (
SELECT (100*COUNT(ID)/'.$total.') percentile
FROM mytable
WHERE rank >= '.$rank.'
) a
ON mytable.ID = a.ID
WHERE mytable.ID='.$ID.'
SET mytable.percentile = a.percentile';
Ce n'est probablement pas le processus le plus efficace, mais il est certainement précis, et comme dans mon cas la valeur du "score" n'est pas mise à jour très souvent, j'exécute le script ci-dessus comme une opération batch cron pour maintenir à jour les rangs centiles.