MySQL - 动态计算百分位数

时间:2012-07-18 14:28:41

标签: mysql

我有一个MySQL SELECT查询,在同一个表中使用20个不同的比较。这是一个例子:

SELECT * FROM mytable
WHERE (col1 > (col2 * 0.25))
AND (col5 < col10) .......

我正在尝试根据返回的SCORE结果中名为SELECT的列的顺序计算百分位数排名。我尝试使用增量行号和COUNT(*)来获取股票的排名和返回的结果总数,但不确定如何在某些结果具有相同SCORE的情况下分配相同的排名。

以下是我要计算的公式:

((COUNT(lower scores) + (COUNT(same/tied scores) / 2)) * 100) / COUNT(total results)

如何在动态计算百分位数的同一行结果中找到较低分数,相同/平局分数和总分数?

我正在尝试避免使用存储过程,因为我希望应用程序的管理员根据需要在我的应用程序管理区域内定制SELECT语句。

3 个答案:

答案 0 :(得分:3)

使用上面的Shlomi代码,这里是我想出的用于计算百分位数排名的代码(如果有人想在将来计算这些代码):

SELECT 
    c.id, c.score, ROUND(((@rank - rank) / @rank) * 100, 2) AS percentile_rank
FROM
    (SELECT 
    *,
        @prev:=@curr,
        @curr:=a.score,
        @rank:=IF(@prev = @curr, @rank, @rank + 1) AS rank
    FROM
        (SELECT id, score FROM mytable) AS a,
        (SELECT @curr:= null, @prev:= null, @rank:= 0) AS b
ORDER BY score DESC) AS c;

答案 1 :(得分:2)

这是(我的)帖子,解释了SELECT期间的排名:SQL: Rank without Self Join

它使用用户定义的变量,即使在迭代行时也会访问和分配这些变量。

使用相同的逻辑,它可以扩展到包括总分数,不同分数等。作为预览,这是一个典型的查询:

SELECT
  score_id, student_name, score,
  @prev := @curr,
  @curr := score,
  @rank := IF(@prev = @curr, @rank, @rank+1) AS rank
FROM
  score,
  (SELECT @curr := null, @prev := null, @rank := 0) sel1
ORDER BY score DESC
;

答案 2 :(得分:1)

Shlomi和Zishan(使用Shlomi的代码)的回答肯定没有给出准确的结果,正如我在一张大桌子上检查结果所发现的那样。正如其他地方所解释的那样,显然不可能在单个MySQL查询中计算百分位数排名: SQL rank percentile

使用用户定义变量的Shlomi Noach方法 - 首先 - 看起来就像它排名前几百分之一的工作正常,但它很快就会降级为低排名表格中的行。像我一样,自己查看数据结果。

请参阅Roland Bouman撰写的博客文章,了解为什么Shlomi在单个SQL语句中使用用户定义变量的方法无效,并提出了更好的解决方案:

http://rpbouman.blogspot.com/2009/09/mysql-another-ranking-trick.html

然后我为此目的修改了Bouman的代码,这是我的解决方案,它必然结合了PHP和MySQL:

步骤1)通过提交以下两个查询来计算并存储每行的绝对等级:

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;

步骤2:获取总行数(并将结果存储在PHP变量$ total中)

SELECT COUNT(ID) FROM mytable

步骤3:使用PHP循环遍历表格,使用每行的绝对等级来计算行的百分位数等级:

3a)循环:

SELECT ID, rank FROM mytable

将这些行值存储为PHP中的$ ID和$ rank

3b)对于每一行:

$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';

可能不是最有效的过程,但绝对准确,因为在我的情况下,得分&#39;值不经常更新,因此我将上述脚本作为cron批处理操作运行,以使百分位数排名保持最新。