我有一个包含数千行的表格,我想计算其中一个名为 "round" 的字段的90百分位数。
例如,选择处于第90百分位的“round”值。
我没有看到在MySQL中直接完成此计算的简单方法。
能否提供一些关于如何开始这种计算的建议?
谢谢!
我有一个包含数千行的表格,我想计算其中一个名为 "round" 的字段的90百分位数。
例如,选择处于第90百分位的“round”值。
我没有看到在MySQL中直接完成此计算的简单方法。
能否提供一些关于如何开始这种计算的建议?
谢谢!
SELECT * FROM
(SELECT t.*, @row_num :=@row_num + 1 AS row_num FROM YOUR_TABLE t,
(SELECT @row_num:=0) counter ORDER BY YOUR_VALUE_COLUMN)
temp WHERE temp.row_num = ROUND (.95* @row_num);
比较解决方案:
在我的服务器上,获取130万行中99%分位数所需的秒数:
0.01秒
0.7秒
2.3秒
1.6秒
5.7秒
对于使用LIMIT x,y
处理大表格的最快解决方案:
SELECT COUNT(*) AS cnt FROM t
n = (cnt - 1) * (1 - 0.95)
:SELECT k FROM t ORDER BY k DESC LIMIT n,1
由于mysql不支持在LIMIT子句中指定变量(除了存储过程),因此此解决方案需要两个查询(可以通过存储过程进行优化)。通常,附加查询开销非常低。
如果将索引添加到k列并且不使用复杂的where子句(例如1百万行的表格中只需0.01秒,因为不需要排序),则可以进一步优化此解决方案。
PHP中的实现示例(可以计算列和表达式的分位数):
function get_percentile($table, $where, $expr, $percentile) {
if ($where) $subq = "WHERE $where";
else $subq = "";
$r = query("SELECT COUNT(*) AS cnt FROM $table $subq");
$w = mysql_fetch_assoc($r);
$num = abs(round(($w['cnt'] - 1) * (100 - $percentile) / 100.0));
$q = "SELECT ($expr) AS prcres FROM $table $subq ORDER BY ($expr) DESC LIMIT $num,1";
$r = query($q);
if (!mysql_num_rows($r)) return null;
$w = mysql_fetch_assoc($r);
return $w['prcres'];
}
// Usage example
$time = get_percentile(
"state", // table
"service='Time' AND cnt>0 AND total>0", // some filter
"total/cnt", // expression to evaluate
80); // percentile
PERCENTILE_DISC
和PERCENTILE_CONT
,以精确地完成此任务。至少在Oracle、PostgreSQL、SQL Server和Teradata中提供了实现。不幸的是,MySQL中没有这个功能。但是,您可以按照以下步骤在MySQL 8中模拟PERCENTILE_DISC
:链接。SELECT DISTINCT first_value(my_column) OVER (
ORDER BY CASE WHEN p <= 0.9 THEN p END DESC /* NULLS LAST */
) x,
FROM (
SELECT
my_column,
percent_rank() OVER (ORDER BY my_column) p,
FROM my_table
) t;
PERCENT_RANK
,给定您的my_column
排序,并找到百分位数小于或等于0.9的最后一行。我曾经尝试解决这个问题很长一段时间,然后我找到了下面的答案。真是太棒了!即使对于大型表格(我使用它的表格包含约500万条记录),速度也相当快,只需要几秒钟。
SELECT
CAST(SUBSTRING_INDEX(SUBSTRING_INDEX( GROUP_CONCAT(field_name ORDER BY
field_name SEPARATOR ','), ',', 95/100 * COUNT(*) + 1), ',', -1) AS DECIMAL)
AS 95th Per
FROM table_name;
你可以想象,只需将table_name和field_name替换为您的表名和列名即可。
如需更多信息,请查看Roland Bouman的原始帖子。
http://www.artfulsoftware.com/infotree/queries.php#68
SELECT
a.film_id ,
ROUND( 100.0 * ( SELECT COUNT(*) FROM film AS b WHERE b.length <= a.length ) / total.cnt, 1 )
AS percentile
FROM film a
CROSS JOIN (
SELECT COUNT(*) AS cnt
FROM film
) AS total
ORDER BY percentile DESC;
这对于非常大的表格可能会很慢
where percentile between 89 and 91
。 - exussumround
的字段,那么可以使用a.round和b.round。 - exussum百分位数最常见的定义是指一个数值,该数值下方所包含的分数比例占据了总体的一定百分比。例如,你在一次考试中得到了90分中的67分。但是,除非你知道自己所处的百分位数,否则这个分数没有实际意义。如果你知道自己的分数处于95分位数,那么就意味着你在参加考试的人中表现比95%的人都要好。
此解决方案同样适用于旧版本MySQL 5.7。
SELECT *, @row_num as numRows, 100 - (row_num * 100/(@row_num + 1)) as percentile
FROM (
select *, @row_num := @row_num + 1 AS row_num
from (
SELECT t.subject, pt.score, p.name
FROM test t, person_test pt, person p, (
SELECT @row_num := 0
) counter
where t.id=pt.test_id
and p.id=pt.person_id
ORDER BY score desc
) temp
) temp2
-- optional: filter on a minimal percentile (uncomment below)
-- having percentile >= 80
ntile
窗口函数:SELECT SomeTable.ID, SomeTable.Round
FROM SomeTable
JOIN (
SELECT SomeTable, (NTILE(100) OVER w) AS Percentile
FROM SomeTable
WINDOW w AS (ORDER BY Round)
) AS SomeTablePercentile ON SomeTable.ID = SomeTablePercentile.ID
WHERE Percentile = 90
LIMIT 1
https://dev.mysql.com/doc/refman/8.0/en/window-function-descriptions.html#function_ntile
SomeTable.ID, (NTILE...
。 - Antonín Hoskovec在MySQL 8中可行的另一种解决方案:生成您的数据的直方图:
ANALYZE TABLE my_table UPDATE HISTOGRAM ON my_column WITH 100 BUCKETS;
然后只需从information_schema.column_statistics中选择第95条记录:
SELECT v,c FROM information_schema.column_statistics, JSON_TABLE(histogram->'$.buckets',
'$[*]' COLUMNS(v VARCHAR(60) PATH '$[0]', c double PATH '$[1]')) hist
WHERE column_name='my_column' LIMIT 95,1
然后就完成了!你仍然需要决定是取百分位数的下限还是上限,或者可能取平均值 - 但这现在只是一个小任务。最重要的是 - 一旦直方图对象建立起来,这非常快速。
此解决方案的来源:lefred's博客。