正确使用 PERCENTILE_CONT - Oracle SQL

Posted

技术标签:

【中文标题】正确使用 PERCENTILE_CONT - Oracle SQL【英文标题】:Properly using PERCENTILE_CONT - Oracle SQL 【发布时间】:2019-07-01 12:41:03 【问题描述】:

我正在尝试计算以下内容:

数据集的平均值 数据集的中位数 数据集的前 20% 数据集底部的 20%

我的数据集如下所示:

| Part | Step | Step_Start | Part_Finish |   TheTime   |
|:----:|:----:|:----------:|:-----------:|:-----------:|
|   1  |  200 |  15-Aug-18 |  19-Jun-19  | 307.4926273 |
|   2  |  200 |  7-Jun-19  |  19-Jun-19  |  11.4434375 |
|   3  |  200 |  17-Sep-18 |   4-Feb-19  | 139.4360417 |
|   4  |  200 |  30-Jan-19 |   4-Feb-19  | 4.356666667 |
|   5  |  200 |  1-Oct-18  |  18-Feb-19  | 139.4528009 |
|   6  |  200 |  13-Feb-19 |  18-Feb-19  |   4.50375   |
|   7  |  200 |  17-Oct-18 |  28-Mar-19  | 161.7007176 |
|   8  |  200 |  12-Nov-18 |  28-Mar-19  |  135.630625 |
|   9  |  200 |  25-Oct-18 |  26-Feb-19  | 123.6026968 |
|  10  |  200 |  22-Feb-19 |  26-Feb-19  | 3.628090278 |
|  11  |  200 |  30-Oct-18 |   3-Jan-19  | 64.51466435 |
|  12  |  200 |  12-Dec-18 |   3-Jan-19  | 21.48703704 |
|  13  |  200 |  15-Nov-18 |  14-Jan-19  | 59.41373843 |
|  14  |  200 |  7-Jan-19  |  14-Jan-19  | 6.621828704 |
|  15  |  200 |  15-Nov-18 |  12-Jan-19  | 57.62283565 |
|  16  |  200 |  8-Jan-19  |  12-Jan-19  | 3.264398148 |
|  17  |  200 |  15-Nov-18 |   7-Mar-19  | 111.5082523 |
|  18  |  200 |  4-Mar-19  |   7-Mar-19  | 2.153587963 |
|  19  |  200 |  16-Nov-18 |  23-May-19  | 187.6931481 |
|  20  |  200 |  16-Nov-18 |   3-Jan-19  | 47.47916667 |
|  21  |  200 |  17-Dec-18 |   3-Jan-19  | 16.62722222 |
|  22  |  200 |  20-Nov-18 |  14-Feb-19  |  85.6115625 |
|  23  |  200 |  9-Feb-19  |  14-Feb-19  | 4.520787037 |
|  24  |  200 |  19-Nov-18 |  14-Jan-19  | 55.53342593 |
|  25  |  200 |  9-Jan-19  |  14-Jan-19  | 4.721400463 |
|  26  |  200 |  26-Nov-18 |   9-Jan-19  | 43.50748843 |
|  27  |  200 |  4-Jan-19  |   9-Jan-19  | 4.417164352 |
|  28  |  200 |  26-Nov-18 |  21-Jan-19  | 55.59988426 |
|  29  |  200 |  13-Jan-19 |  21-Jan-19  |    7.535    |
|  30  |  200 |  16-Jan-19 |  21-Jan-19  | 4.618796296 |
|  31  |  200 |  26-Nov-18 |  11-Jan-19  | 45.42148148 |
|  32  |  200 |  4-Jan-19  |  11-Jan-19  | 6.316921296 |
|  33  |  200 |  4-Dec-18  |  24-Jan-19  |  50.3669213 |
|  34  |  200 |  18-Jan-19 |  24-Jan-19  | 5.589467593 |
|  35  |  200 |  4-Dec-18  |  31-Jan-19  | 57.26877315 |
|  36  |  200 |  22-Jan-19 |  31-Jan-19  | 8.240034722 |
|  37  |  200 |  5-Dec-18  |  28-Jun-19  | 204.5283912 |
|  38  |  200 |  26-Jun-19 |  28-Jun-19  | 1.508252315 |
|  39  |  200 |  9-Feb-19  |  19-Feb-19  | 9.532893519 |
|  40  |  200 |  7-Dec-18  |  14-Feb-19  | 68.51900463 |
|  41  |  200 |  5-Feb-19  |  14-Feb-19  | 8.641076389 |
|  42  |  200 |  11-Dec-18 |  25-Jan-19  | 44.50501157 |
|  43  |  200 |  22-Jan-19 |  25-Jan-19  | 2.511435185 |
|  44  |  200 |  13-Dec-18 |  17-Jan-19  | 34.43806713 |
|  45  |  200 |  14-Jan-19 |  17-Jan-19  | 2.210972222 |
|  46  |  200 |  13-Dec-18 |  24-Jan-19  | 41.38921296 |
|  47  |  200 |  17-Jan-19 |  24-Jan-19  | 6.444664352 |
|  48  |  200 |  10-Jan-19 |   7-Feb-19  | 27.43130787 |
|  49  |  200 |  1-Feb-19  |   7-Feb-19  | 5.349189815 |
|  50  |  200 |  18-Dec-18 |   4-Feb-19  | 47.50416667 |
|  51  |  200 |  29-Jan-19 |   4-Feb-19  | 5.481979167 |
|  52  |  200 |  3-Jan-19  |  30-Jan-19  | 26.46112269 |
|  53  |  200 |  23-Jan-19 |  30-Jan-19  | 6.712175926 |
|  54  |  200 |  4-Jan-19  |   5-Feb-19  | 31.49590278 |
|  55  |  200 |  30-Jan-19 |   5-Feb-19  | 5.385798611 |
|  56  |  200 |  23-Jan-19 |  20-Mar-19  |  55.296875  |
|  57  |  200 |  21-Feb-19 |  20-Mar-19  | 26.06854167 |
|  58  |  200 |  22-Jan-19 |  14-Mar-19  | 50.57989583 |
|  59  |  200 |  8-Mar-19  |  14-Mar-19  | 5.147303241 |
|  60  |  200 |  22-Jan-19 |  21-Feb-19  | 29.46405093 |
|  61  |  200 |  14-Feb-19 |  21-Feb-19  | 6.701724537 |
|  62  |  200 |  24-Jan-19 |  23-Apr-19  | 88.50689815 |
|  63  |  200 |  17-Apr-19 |  23-Apr-19  | 5.725405093 |
|  64  |  200 |  28-Jan-19 |  21-Feb-19  | 23.50082176 |
|  65  |  200 |  13-Feb-19 |  21-Feb-19  | 7.115717593 |
|  66  |  200 |  31-Jan-19 |  28-Feb-19  | 27.55881944 |
|  67  |  200 |  25-Feb-19 |  28-Feb-19  | 2.633738426 |
|  68  |  200 |  31-Jan-19 |  27-Feb-19  | 26.46105324 |
|  69  |  200 |  23-Feb-19 |  27-Feb-19  | 3.531423611 |
|  70  |  200 |  1-Feb-19  |  28-Feb-19  | 26.45835648 |
|  71  |  200 |  27-Feb-19 |  28-Feb-19  | 0.471296296 |
|  72  |  200 |  6-Feb-19  |  27-Feb-19  | 20.54436343 |
|  73  |  200 |  23-Feb-19 |  27-Feb-19  | 3.598854167 |
|  74  |  200 |  6-Feb-19  |   5-Mar-19  | 26.54347222 |
|  75  |  200 |  28-Feb-19 |   5-Mar-19  | 4.303773148 |
|  76  |  200 |  12-Feb-19 |   6-Mar-19  | 21.56993056 |
|  77  |  200 |  1-Mar-19  |   6-Mar-19  | 4.597615741 |
|  78  |  200 |  12-Feb-19 |  14-Mar-19  | 29.50417824 |
|  79  |  200 |  7-Mar-19  |  14-Mar-19  | 6.083541667 |
|  80  |  200 |  28-Feb-19 |  28-Mar-19  |  27.5291088 |
|  81  |  200 |  25-Mar-19 |  28-Mar-19  | 2.637824074 |
|  82  |  200 |  29-Jan-19 |  28-Feb-19  | 29.34280093 |
|  83  |  200 |  21-Feb-19 |  28-Feb-19  | 6.233831019 |
|  84  |  200 |  19-Feb-19 |  30-Apr-19  | 69.51832176 |
|  85  |  200 |  7-Feb-19  |   5-Mar-19  | 25.74865741 |
|  86  |  200 |  27-Feb-19 |   5-Mar-19  | 5.380034722 |
|  87  |  200 |  21-Feb-19 |  21-Mar-19  | 27.56310185 |
|  88  |  200 |  19-Mar-19 |  21-Mar-19  | 1.161828704 |
|  89  |  200 |  26-Feb-19 |  28-Mar-19  | 29.41315972 |
|  90  |  200 |  22-Mar-19 |  28-Mar-19  | 5.673703704 |
|  91  |  200 |  26-Feb-19 |  28-Mar-19  |  29.5131713 |
|  92  |  200 |  20-Mar-19 |  28-Mar-19  | 7.073414352 |
|  93  |  200 |  28-Feb-19 |  15-Apr-19  | 45.63513889 |
|  94  |  200 |  5-Apr-19  |  15-Apr-19  | 9.479456019 |
|  95  |  200 |  1-Mar-19  |  29-Mar-19  | 27.54568287 |
|  96  |  200 |  25-Mar-19 |  29-Mar-19  | 3.044340278 |
|  97  |  200 |  4-Mar-19  |  27-Mar-19  | 22.52392361 |
|  98  |  200 |  21-Mar-19 |  27-Mar-19  | 5.074421296 |
|  99  |  200 |  14-Feb-19 |  19-Mar-19  | 32.54349537 |
|  100 |  200 |  13-Mar-19 |  19-Mar-19  | 5.265266204 |

我当前的 SQL 查询如下所示:

SELECT
    Step,
    ROUND(MEDIAN(Part_Finish - Step_Start), 2) AS "The_Median",
    ROUND(AVG(Part_Finish - Step_Start), 2) AS "The_Average",
    PERCENTILE_CONT(0.20) WITHIN GROUP (ORDER BY (Part_Finish - Step_Start) ASC) AS "Best_Time",
    PERCENTILE_CONT(0.80) WITHIN GROUP (ORDER BY (Part_Finish - Step_Start) ASC) AS "Worst_Time"

FROM
    myTbl

GROUP BY
    Step

但是,我不确定我的结果是否正确,因为我认为我没有正确使用PERCENTILE_CONT()。如何使用PERCENTILE_CONT()(或其他方法)根据最好的 20% 数据和最差的 20% 数据找到平均或中位数(以更容易的为准)“完成时间”?

我希望一些结果看起来像这样:

| Step | The_Average | The_Median | Best_Time | Worst_Time |
|:----:|:-----------:|:----------:|:---------:|:----------:|
|  200 |  < value >  |  < value > | < value > |  < value > |

其中&lt; value &gt; 字段是正确计算的数据集的平均值、中值以及最佳和最差。通过查找前 20% 数据(即最小时间)或最差 20% 数据(即最大时间)的平均值或中位数来计算最佳和最差

【问题讨论】:

要求有点不清楚,至少对我来说是这样。您能否edit您的问题并添加预期结果? 添加了@Mureinik 您能否edit 您的帖子,以便将数据作为 DDL/DML 语句(或 DB<>FIDDLE)提供,以便我们可以轻松使用它? (而不必手动将其编辑成可用的东西)。 这个问题没有多大意义——在数学上(与编程无关)。您的数据集要么是参数化的,要么不是。在第一种情况下,您会关心平均值,而在后一种情况下,您会关心中位数。对于同一数据集,您会发现两者都有实际用途的情况非常罕见。更糟糕的是:如果您隔离前 20% 或后 20%(非参数统计),然后取这些值的 平均值,您将在同一计算中混合参数和非参数统计;统计入门课程失败的原因。 当然,要求中位数以及第 20 和第 80 个百分位数确实有意义。然后在顶部和底部 20% 内,您可能会要求 中位数(但不是 平均数 - 这没有意义)。不过,要点是,根据定义前 20% 或后 20% 的中位数恰好分别是第 10 位。数据集的第 90 个百分位;它们可以直接计算,不需要复杂的计算。 【参考方案1】:

PERCENTILE_CONT 是一个窗口函数,因此如果您只想要一个由具有标量值的单个记录组成的结果集,您可以尝试选择 distinct:

SELECT DISTINCT
    PERCENTILE_CONT(0.5) WITHIN GROUP (ORDER BY Part_Finish - Step_Start) AS "The_median",
    ROUND(AVG(Part_Finish - Step_Start) OVER (ORDER BY Part_Finish - Step_Start), 2) AS "The_Average",
    PERCENTILE_CONT(0.20) WITHIN GROUP (ORDER BY Part_Finish - Step_Start) AS "Best_Time",
    PERCENTILE_CONT(0.80) WITHIN GROUP (ORDER BY Part_Finish - Step_Start) AS "Worst_Time"
FROM myTbl;

上述方法的原因是在整个表格上选择 PERCENTILE_CONT(一个窗口函数)只会将整个表格作为结果集返回。但是,当您使用它时,每条记录的值总是相同的。因此,我们可以只取不同的值来得到一个结果。

如果您希望每个 Step 值都有不同的报告,那么您应该在对 PERCENTILE_CONT 的调用中使用 PARTITION BY,例如

PERCENTILE_CONT(0.5) WITHIN GROUP (PARTITION BY Step
    ORDER BY Part_Finish - Step_Start) AS "The_median"

【讨论】:

这太好了,非常感谢。有没有办法使用这个答案来进一步扩展窗口函数的分析,比如找到顶部和底部 20% 的平均值? @JerryM。这与您提出的问题确实不同。 好的,那我再发一个问题,谢谢你的帮助。 这是完全错误的。 PERCENTILE_CONT 既是聚合函数又是分析函数。您在答案的第一部分中编写它的方式是一个聚合函数。如果没有GROUP BY 子句,每个PERCENTILE_CONT 函数将在整个表中只返回一个值;不需要DISTINCT 更重要的是,在OP的问题下查看我的cmets。

以上是关于正确使用 PERCENTILE_CONT - Oracle SQL的主要内容,如果未能解决你的问题,请参考以下文章

红移中的 PERCENTILE_CONT()

BigQuery:标准 SQL 和 PERCENTILE_CONT() 函数

在 Vertica 中创建 percentile_cont 作为聚合函数

Percentile_Cont 函数抛出错误

apache spark sql中的等效percentile_cont函数

sql server 2008 中 percentile_cont 的替代方案