18


7

MySQLには非常に大きな測定データの表があり、これらの値のそれぞれについてパーセンタイルランクを計算する必要があります。 Oracleにはpercent_rankという関数があるようですが、MySQLに似たものは見つかりません。 私はとにかくテーブルにデータを入れるためにそれを使用するPythonでそれを強引に強制することができたことを確かに確かめたが、一つのサンプルが200.000の観察を持つかもしれないので私はそれが非常に非効率的になると思う。

9 Answer


19


これは参加を必要としない別のアプローチです。 私の場合(15,000行のテーブル)、それは約3秒で実行されます。 (JOINメソッドは一桁長くかかります)。

サンプルでは、​​_measure_がパーセントランクを計算している列で、_id_が単なる行識別子であるとします(必須ではありません)。

SELECT id、@ prev:= @curr(前の文字列として)、@curr:=測定値の現在位置、@rank:= IF(@prev> @curr、@rank @ties、@rank)ASランク、@ties:= IF(@ prev = @curr、@ties 1、1)ASとして、(1- @ rank / @ total)mytableからのpercentrankとして、(SELECT @curr:= null、@prev:= null、@rank:= 0、@ties) := 1、@total:= measureがNULLではないmytableからのcount(*)b WHERE measureがNULLではないORDER BY measure DESC

この方法の功績はShlomi Noachです。 彼はそれについてここで詳細に書いている:

私はこれをMySQLでテストしましたが、うまく機能します。 Oracle、SQLServer、その他については知らない


2


これは比較的醜い答えです、そして私はそれを言って有罪を感じます。 それは言った、それはあなたの問題を解決するのに役立つかもしれません。

パーセンテージを決定する1つの方法は、すべての行を数え、指定した数より大きい行数を数えることです。 より大きいか小さいかを計算し、必要に応じて逆数を取ることができます。

あなたの番号にインデックスを作成します。 合計=選択数(); less_equal = select count()ここで、value> indexed_number;

割合は、less_equal / totalまたは(total - less_equal)/ totalのようになります。

両方が、作成したインデックスを使用していることを確認してください。 そうでない場合は、なるまで微調整します。 Explain照会の右側の列には "using index"があります。 select count(*)の場合、InnoDBにはindexを使い、MyISAMにはconstのようなものを使うべきです。 MyISAMはこの値を計算せずにいつでも知ることができます。

データベースにパーセンテージを格納する必要がある場合は、パフォーマンスのために上記の設定を使用してから、2番目のクエリを内部選択として使用して各行の値を計算できます。 最初のクエリの値は定数として設定できます。

これは助けになりますか?

ヤコブ


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";
        }
    }
}


2


SELECT c.id、c.score、ROUND((((@ランク - ランク)/ @ランク)* 100、2)AS percentile_rank FROM(SELECT *、@prev:= @ curr、@curr:= a.score、@ rank:= IF(@prev = @curr、@rank、@rank 1)ASランクFROM(SELECT ID、mytableからのスコア)AS、、(SELECT @curr:= null、@prev:= null、@rank:= 0)AS b順にスコアDESC)AS c;


1


MySQL 8はついにウィンドウ関数を導入しました。その中でもhttps://dev.mysql.com/doc/refman/8.0/en/window-function-descriptions.html#function_percent-rank [+ 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およびウィンドウ関数を使用すると、https://blog.jooq.org/2019/01/28/how-to-emulate-percentile_disc-in-mysql-and-other-rdbms/ [[ PERCENTILE_DISC ]をエミュレートできます。ただし、 ` PERCENT_RANK `および ` FIRST_VALUE +`ウィンドウ関数を再度使用します]。


0


ランクを取得するには、テーブルの外側を(左)外部結合する必要があるとします。

テーブルt1からt1.name、t1.value、count(distinct isnull(t2.value、0))を選択

各行について、同じテーブルの(あるとしても)いくつの行に低い値があるかを数えます。

私はsqlserverに慣れているので構文が正しくないかもしれないことに注意してください。 また、別の人はあなたが達成したいことのために正しい行動を持っていないかもしれません。 しかし、それが一般的な考え方です。 次に、実際のパーセンタイルランクを取得するには、まず変数内の値の数(または使用する規則によって異なる値)を取得し、上記の実際のランクを使用してパーセンタイルランクを計算する必要があります。


0


次のような売上表があるとします。

user_id、単位

次のクエリは各ユーザーのパーセンタイルを与えます。

a。

これは交差結合になるのでO(n 2)の複雑さを招くので最適化されていない解決策と見なすことができますが、mysqlバージョンには関数がないとすれば単純に思えます。


-1


操作が「パーセンタイルランク」によって何を意味しているのかわからないが、値のセットのために与えられたパーセンタイルを得るためにhttp://rpbouman.blogspot.com/2008/07/calculating-nth-percentile-in-mysql.html SQL計算は、別の、または複数の百分位数を生成するように簡単に変更できます。

1つのメモ:私は計算をわずかに変えなければならなかった、例えば90パーセンタイル - "90/100 * COUNT()1"の代わりに "90/100 * COUNT()0.5" パーセンタイルの次に高い値を選択する代わりに、順序付きリストのパーセンタイルポイントを過ぎて2つの値をスキップすることがありました。 mysqlで整数丸めが機能する方法かもしれません。

すなわち:

  1. SUBSTRING_INDEX(SUBSTRING_INDEX( GROUP_CONCAT(fieldValue ORDER BY fieldValue SEPARATOR '、')、 '、'、90/100 * COUNT( 0.5 *)、 '、'、-1)90thパーセンタイルとして…