23

MySQLには非常に大きな測定データのテーブルがあり、これらの値のすべてのパーセンタイルランクを計算する必要があります。Oracleにはpercent_rankという関数があるようですが、MySQLに似たものは見つかりません。確かに、Pythonでブルートフォース攻撃を行うことができます。これは、とにかくテーブルにデータを入力するために使用しますが、1つのサンプルに200.000の観測値がある可能性があるため、これは非常に非効率的だと思います。

4

8 に答える 8

20

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

サンプルでは、​​メジャーがパーセント ランクを計算する列であり、idが単なる行識別子であると仮定します (必須ではありません)。

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などについてはわかりません。

于 2011-10-25T03:18:16.533 に答える
4

これを行う簡単な方法はありません。http://rpbouman.blogspot.com/2008/07/calculating-nth-percentile-in-mysql.htmlを参照してください。

于 2009-06-29T07:58:08.877 に答える
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";
        }
    }
}
于 2011-03-25T05:21:30.683 に答える
2

MySQL 8 では、ついにウィンドウ関数が導入されました。その中で、PERCENT_RANK()あなたが探していた関数です。だから、ただ書いてください:

SELECT col, percent_rank() OVER (ORDER BY col)
FROM t
ORDER BY col

あなたの質問は「パーセンタイル」に言及していますが、これは少し異なるものです。完全を期すために、SQL 標準と一部の RBDMS (Oracle、PostgreSQL、SQL Server、Teradata) には逆分布関数がありますが、MySQL にはありませんPERCENTILE_DISCPERCENTILE_CONTMySQL 8 とウィンドウ関数を使用すると、をエミュレートできますが、ここでもおよびウィンドウ関数PERCENTILE_DISCを使用しPERCENT_RANKFIRST_VALUEます。

于 2019-01-28T09:36:34.337 に答える
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に精通しているため、構文が正しくない可能性があることに注意してください。また、明確なものは、あなたが達成したいことに対して正しい振る舞いをしていないかもしれません。しかし、それが一般的な考え方です。
次に、実際のパーセンタイルランクを取得するには、最初に変数内の値の数(または、使用する規則に応じて個別の値)を取得し、上記の実際のランクを使用してパーセンタイルランクを計算する必要があります。

于 2009-08-21T08:39:00.870 に答える
0

次のような販売テーブルがあるとします。

ユーザー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 バージョンには機能がないことを考えると単純に見えることに注意してください。

于 2018-11-15T14:17:26.100 に答える