我在MySQL中有一个非常大的测量数据表,我需要为这些值中的每一个计算百分等级。 Oracle似乎有一个名为percent_rank的函数,但我找不到类似MySQL的东西。当然,我可以在Python中强制使用它,但我仍然使用它来填充表格,但我怀疑这样做效率很低,因为一个样本可能有200,000个观察值。
答案 0 :(得分:19)
这是一种不需要连接的不同方法。在我的情况下(一个包含15,000+的表)行,它在大约3秒内运行。 (JOIN方法需要更长的时间)。
在示例中,假设 measure 是您计算百分比排名的列,而 id 只是行标识符(不是必需的):< / p>
SELECT
id,
@prev := @curr as prev,
@curr := measure as curr,
@rank := IF(@prev > @curr, @rank+@ties, @rank) AS rank,
@ties := IF(@prev = @curr, @ties+1, 1) AS ties,
(1-@rank/@total) as percentrank
FROM
mytable,
(SELECT
@curr := null,
@prev := null,
@rank := 0,
@ties := 1,
@total := count(*) from mytable where measure is not null
) b
WHERE
measure is not null
ORDER BY
measure DESC
这种方法的功劳归于Shlomi Noach。他在这里详细描述了它:
http://code.openark.org/blog/mysql/sql-ranking-without-self-join
我在MySQL中对此进行了测试,效果很好;不知道Oracle,SQLServer等。
答案 1 :(得分:4)
答案 2 :(得分:4)
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;
答案 3 :(得分:2)
这是一个相对丑陋的答案,我感到内疚。也就是说,它可以帮助您解决问题。
确定百分比的一种方法是计算所有行,并计算大于您提供的行数的行数。你可以计算大于或小于,并根据需要采取反向。
为您的号码创建索引。 total =选择计数(); less_equal = select count()其中value&gt; indexed_number;
百分比如下:less_equal / total或(total - less_equal)/ total
确保它们都使用您创建的索引。如果不是,请调整它们直到它们为止。解释查询应该在右侧列中具有“使用索引”。在select count(*)的情况下,它应该使用InnoDB的索引和类似于MyISAM的const。 MyISAM可以随时知道这个值而无需计算它。
如果需要将百分比存储在数据库中,可以使用上面的设置来提高性能,然后使用第二个查询作为内部选择来计算每行的值。第一个查询的值可以设置为常量。
这有帮助吗?
雅各
答案 4 :(得分:2)
如果您将SQL与PHP等过程语言结合使用,则可以执行以下操作。这个例子将多余的飞行拦截时间分解为机场,进入他们的百分位数。将MySQL中的LIMIT x,y子句与ORDER BY
结合使用。不是很漂亮,但做的工作(抱歉格式化困难):
$startDt = "2011-01-01";
$endDt = "2011-02-28";
$arrPort= 'JFK';
$strSQL = "SELECT COUNT(*) as TotFlights FROM FIDS where depdt >= '$startDt' And depdt <= '$endDt' and ArrPort='$arrPort'";
if (!($queryResult = mysql_query($strSQL, $con)) ) {
echo $strSQL . " FAILED\n"; echo mysql_error();
exit(0);
}
$totFlights=0;
while($fltRow=mysql_fetch_array($queryResult)) {
echo "Total Flights into " . $arrPort . " = " . $fltRow['TotFlights'];
$totFlights = $fltRow['TotFlights'];
/* 1906 flights. Percentile 90 = int(0.9 * 1906). */
for ($x = 1; $x<=10; $x++) {
$pctlPosn = $totFlights - intval( ($x/10) * $totFlights);
echo "PCTL POSN for " . $x * 10 . " IS " . $pctlPosn . "\t";
$pctlSQL = "SELECT (ablk-sblk) as ExcessBlk from FIDS where ArrPort='" . $arrPort . "' order by ExcessBlk DESC limit " . $pctlPosn . ",1;";
if (!($query2Result = mysql_query($pctlSQL, $con)) ) {
echo $pctlSQL . " FAILED\n";
echo mysql_error();
exit(0);
}
while ($pctlRow = mysql_fetch_array($query2Result)) {
echo "Excess Block is :" . $pctlRow['ExcessBlk'] . "\n";
}
}
}
答案 5 :(得分:1)
MySQL 8最终引入了窗口函数,其中包括您正在寻找的PERCENT_RANK()
函数。因此,只需编写:
SELECT col, percent_rank() OVER (ORDER BY col)
FROM t
ORDER BY col
您的问题提到“百分位数”,这是稍有不同的东西。为了完整起见,SQL标准和某些RBDMS(Oracle,PostgreSQL,SQL Server,Teradata)中有PERCENTILE_DISC
和PERCENTILE_CONT
逆分布函数,而MySQL中没有。使用MySQL 8和窗口函数you can emulate PERCENTILE_DISC
, however, again using the PERCENT_RANK
and FIRST_VALUE
window functions。
答案 6 :(得分:0)
要获得排名,我会说你需要(左)外连接表本身就像:
select t1.name, t1.value, count(distinct isnull(t2.value,0))
from table t1
left join table t2
on t1.value>t2.value
group by t1.name, t1.value
对于每一行,您将计算同一个表中有多少(如果有)行具有较低的值。
请注意,我对sqlserver更熟悉,因此语法可能不正确。此外,对于您想要实现的目标,distinct可能没有正确的行为。但这是一般的想法 然后,要获得真正的百分位数,您需要首先获取变量中的值的数量(或根据您要采用的约定的不同值),并使用上面给出的实际等级计算百分位数。
答案 7 :(得分:0)
假设我们有一个销售表,例如:
user_id,单位
然后下面的查询将给出每个用户的百分比:
select a.user_id,a.units,
(sum(case when a.units >= b.units then 1 else 0 end )*100)/count(1) percentile
from sales a join sales b ;
请注意,这将用于交叉连接,因此会导致O(n2)复杂性,因此可以视为未优化的解决方案,但鉴于我们在mysql版本中没有任何功能,这似乎很简单。
答案 8 :(得分:-1)
不确定“百分等级”是什么意思,但是要获取一组值的给定百分位数,请参见http://rpbouman.blogspot.com/2008/07/calculating-nth-percentile-in-mysql.html sql计算可以很容易地更改为产生另一个或多个百分位数。
一个注意事项:我必须稍微更改计算,例如,第90个百分位数-“ 90/100 * COUNT(*)+ 0.5”,而不是“ 90/100 * COUNT(*)+ 1”。有时,它跳过了有序列表中百分比点之后的两个值,而不是为百分比选择下一个更高的值。也许整数舍入在mysql中的工作方式。
即:
.... SUBSTRING_INDEX(SUBSTRING_INDEX(GROUP_CONCAT(fieldValue ORDER BY fieldValue SEPARATOR','),',',90/100 * COUNT(*)+ 0.5 ),',', -1)作为90thPercentile ....