سؤال

لدي جدول كبير جدًا لبيانات القياس في MySQL وأحتاج إلى حساب الترتيب المئوي لكل واحدة من هذه القيم.يبدو أن Oracle لديها وظيفة تسمى بالمائة_رانك ولكن لا يمكنني العثور على أي شيء مشابه لـ MySQL.من المؤكد أنه يمكنني استخدام القوة الغاشمة في لغة بايثون والتي أستخدمها على أي حال لملء الجدول ولكني أظن أن ذلك سيكون غير فعال تمامًا لأن عينة واحدة قد تحتوي على 200.000 ملاحظة.

هل كانت مفيدة؟

المحلول

هذه إجابة قبيحة نسبيًا، وأشعر بالذنب عندما أقولها.ومع ذلك، قد يساعدك ذلك في حل مشكلتك.

تتمثل إحدى طرق تحديد النسبة المئوية في حساب كافة الصفوف، وحساب عدد الصفوف الأكبر من العدد الذي قدمته.يمكنك حساب إما أكبر أو أقل من وأخذ المعكوس حسب الضرورة.

إنشاء فهرس على رقمك.الإجمالي = حدد العدد ();less_equal = حدد العد () حيث القيمة > Indexed_number؛

ستكون النسبة مثل:less_equal / الإجمالي أو (الإجمالي - less_equal) / الإجمالي

تأكد من أن كليهما يستخدمان الفهرس الذي قمت بإنشائه.إذا لم تكن كذلك، قم بتعديلها حتى تكون كذلك.يجب أن يحتوي الاستعلام التوضيحي على "استخدام الفهرس" في العمود الأيمن.في حالة تحديد العد (*) يجب أن يستخدم مؤشر InnoDB وشيئًا مثل const لـ MyISAM.سيعرف MyISAM هذه القيمة في أي وقت دون الحاجة إلى حسابها.

إذا كنت بحاجة إلى تخزين النسبة المئوية في قاعدة البيانات، فيمكنك استخدام الإعداد من الأعلى للأداء ثم حساب القيمة لكل صف باستخدام الاستعلام الثاني كتحديد داخلي.يمكن تعيين قيمة الاستعلام الأول كقيمة ثابتة.

هل هذا يساعد؟

يعقوب

نصائح أخرى

وهنا مقاربة مختلفة لا تتطلب الانضمام. في حالتي (جدول مع 15،000+) الصفوف، فإنه يعمل في حوالي 3 ثواني. (طريقة التسجيل يأخذ أمر من حجم أطول).

في العينة، افترض أن <م> مقياس هو العمود الذي كنت حساب رتبة في المئة، و<م> معرف هو مجرد معرف صف (غير مطلوب):

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

والائتمان لهذا الأسلوب يذهب إلى شلومي نوح. يكتب عن ذلك بالتفصيل هنا:

http://code.openark.org/blog/mysql/sql-ranking -without الذاتي الانضمام

ولقد اختبرت ذلك في الخلية ويعمل كبيرة. أي فكرة عن أوراكل، سيكلسرفير، وما إلى ذلك.

ولا توجد وسيلة سهلة للقيام بذلك. يمكنك الاطلاع على http://rpbouman.blogspot.com/2008 /07/calculating-nth-percentile-in-mysql.html

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;

إذا كنت الجمع بين SQL باستخدام لغة إجرائية مثل PHP، يمكنك القيام بما يلي. هذا المثال ينهار مرات كتلة رحلة الزائدة في المطار، في النسب المئوية الخاصة بهم. يستخدم LIMIT س، ص بند في الخلية بالاشتراك مع 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";
        }
    }
}

والخلية 8 وظائف نافذة عرض أخيرا، وفيما بينها، في PERCENT_RANK() العمل الذي كنت تبحث عنه. لذلك، والكتابة فقط:

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

وسؤالك يذكر "المئوية"، والتي هي شيء مختلف قليلا. وتوخيا للاكتمال، هناك وظائف توزيع معكوس PERCENTILE_DISC وPERCENTILE_CONT في مستوى SQL وفي بعض RBDMS (أوراكل، الإنترنت، SQL Server و مقاومه)، ولكن ليس في الخلية. مع الخلية 8 وظائف النافذة، <وأ href = "https://blog.jooq.org/2019/01/28/how-to-emulate-percentile_disc-in-mysql-and-other-rdbms/" يختلط = " نوفولو noreferrer "> يمكنك محاكاة PERCENTILE_DISC، ولكن، مرة أخرى باستخدام وظائف PERCENT_RANK ونافذة FIRST_VALUE .

لتحصل على رتبة، وانا اقول تحتاج إلى (يسار) صلة خارجية الجدول على نفسه شيئا مثل:

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 

لكل صف، سوف نحصي عدد (إن وجدت) صفوف الجدول نفسه لها قيمة أدنى.

لاحظ أن أنا أكثر دراية سيكلسرفير ذلك بناء الجملة قد لا يكون على حق. أيضا متميزة قد لا يكون السلوك الصحيح لما تريد تحقيقه. ولكن هذه هي الفكرة العامة.
ثم الحصول على رتبة المئوية الحقيقية سوف تحتاج إلى أولا الحصول على عدد من القيم في متغير (أو قيم مميزة اعتمادا على اتفاقية كنت تريد أن تأخذ) وحساب رتبة المئوية باستخدام رتبة الحقيقية المذكورة أعلاه.

ونفترض أن لدينا جدول المبيعات مثل:

و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) التعقيد بحيث يمكن اعتباره حلا غير محسن ولكن يبدو بسيطا نظرا ليس لدينا أي وظيفة في الإصدار ماي.

لست متأكدًا مما تعنيه العملية بـ "الرتبة المئوية"، ولكن للحصول على نسبة مئوية معينة لمجموعة من القيم، راجع http://rpbouman.blogspot.com/2008/07/calculating-nth-percentile-in-mysql.htmlيمكن بسهولة تغيير حساب SQL لإنتاج نسب مئوية أخرى أو متعددة.

ملاحظة واحدة:اضطررت إلى تغيير الحساب قليلاً، على سبيل المثال النسبة المئوية التسعين - "90/100 * COUNT(*) + 0.5" بدلاً من "90/100 * COUNT(*) + 1".في بعض الأحيان، كان يتم تخطي قيمتين بعد النقطة المئوية في القائمة المرتبة، بدلاً من اختيار القيمة الأعلى التالية للنقطة المئوية.ربما الطريقة التي يعمل بها تقريب الأعداد الصحيحة في الخلية.

أي:

....SUBSTRING_INDEX(SUBSTRING_INDEX( GROUP_CONCAT(fieldValue ORDER BY fieldValue SEPARATOR ','), ',', 90/100 * COUNT(*) + 0.5)، '،'، -1) كـ 90thPercentile ....

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top