Percentile_disc()表示非圆值

时间:2017-07-04 07:26:25

标签: oracle percentile

我试图找到一个没有运气的解决方案。

在我的查询中,我选择count(*)percentile_disc(.9)来找到第90个位置。 案例是,当计数为29时,第90个百分位数比第27个百分位数更接近26但仍然返回第27个目标。

有任何方式可以说,如果5< Nth< 10将结果减少一个?

参考表

ID    Count    90th
-------------------
1     50       45
2     40       36
3     27       25     <-- Should be 24
4      9        9     <-- Should be  8

9的90%是0.9,它应该删除1并产生8。

---直到这里是我对第N百分位数的理解---

现在我拥有:

我的表有一个条目的丢失(当天+ 100k)所以我想每天运行这个查询。

Service_id   start_time      end_time
-------------------------------------
Service1    1499025651614    1499025651648
Service2    1499025655145    1499025655434
Service3    1499025656029    1499025656112
Service2    1499025658755    1499025659135
Service3    1499025726862    1499025728346
Service1    1499025748782    1499025750032
Service3    1499025749277    1499025749900
Service3    1499025757681    1499025758517
Service2    1499025775000    1499025775101
Service1    1499025785556    1499025785633
...

我有一个查询来为每项服务选择最小值,最大值和平均值

 select mt.SERVICE_ID as SERVICE_ID,
           count(*) as COUNT,
           round(avg((mt.end_time - mt.start_time) / 1000), 2) as Avg,
           round(min((mt.end_time - mt.start_time) / 1000), 2) AS Min,
           round(max((mt.end_time - mt.start_time) / 1000), 2) AS Max
      from myTable mt
     group by mt.service_id

我希望在使用联接之前纳入第90个百分位数。

select service_id, round(percentile_disc(.90) within group(order by elapsed), 2) as perc
from (select mt.service_id, ((mt.end_time - mt.start_time) / 1000) as elapsed
      from myTable mt)
group by service_id

当计数是(假设)9时出现问题,在这种情况下,MAXPerc是相同的(由于百分位数没有删除任何东西),但我需要这个特殊情况,删除最后一个,给我作为结果在第8位的调整。

在这种情况下,还有什么方法可以删除一个位置吗?

1 个答案:

答案 0 :(得分:0)

PERCENTILE_DISC()并不能完全按照您的想法行事。

Oracle Documentation

  

<强>目的

     

PERCENTILE_DISC是一个逆分布函数,它假设一个离散分布模型。它需要百分位值和排序规范,并从集合中返回一个元素。在计算中忽略空值。

     

...

     

对于给定的百分位值PPERCENTILE_DISC对ORDER BY子句中的表达式值进行排序,并返回值CUME_DIST最小的值(相对于同一种类)规范)大于或等于P

     

分析示例

     

以下示例计算样本表hr.employees中每位员工薪水的中位数离散百分位数:

SELECT last_name, salary, department_id,
   PERCENTILE_DISC(0.5) WITHIN GROUP (ORDER BY salary DESC)
      OVER (PARTITION BY department_id) "Percentile_Disc",
   CUME_DIST() OVER (PARTITION BY department_id 
      ORDER BY salary DESC) "Cume_Dist"
FROM employees where department_id in (30, 60);

LAST_NAME         SALARY DEPARTMENT_ID Percentile_Disc  Cume_Dist
------------- ---------- ------------- --------------- ----------
Raphaely           11000            30            2900 .166666667
Khoo                3100            30            2900 .333333333
Baida               2900            30            2900         .5
Tobias              2800            30            2900 .666666667
Himuro              2600            30            2900 .833333333
Colmenares          2500            30            2900          1
Hunold              9000            60            4800         .2
Ernst               6000            60            4800         .4
Austin              4800            60            4800         .8
Pataballa           4800            60            4800         .8
Lorentz             4200            60            4800          1
     

部门30的中值是2900,这是其值   相应的百分位数(Cume_Dist)是最大值   大于或等于0.5。部门60的中值是4800,   这是相应百分位数最小的值   值大于或等于0.5。

在他们在文档中提供的示例中,如果百分位数设置为0.9(而不是0.5),那么您可以看到CUME_DIST来自0.81(对于部门60),PERCENTILE_DISC(0.9) ...会给4200,因为这是最小CUME_DIST大于或等于0.9的值。要获得倒数第二个值,在这种情况下需要0.8的百分位数。

  

问题出现时,计数是(假设)9,在这种情况下,MAX和Perc是相同的(由于百分位数没有删除任何东西)但我需要在这种特殊情况下删除最后一个给我作为结果在第8位的时间。

对于9个项目,每行的CUME_DIST值将为:

ROW_NUMBER CUME_DIST
---------- ---------
         1      .111
         2      .222
         3      .333
         4      .444
         5      .556
         6      .667
         7      .778
         8      .889
         9     1.000

如果您使用PERCENTILE_DISC( 0.9 ),那么它会查找最大CUME_DIST大于或等于该值的值 - 只有一个值1.000也是最大值。

如果您想要不同的值,则需要使用较低的百分位数。

<强>更新

您可以尝试这样的事情:

select service_id, 
       elapsed as perc
from (
  select service_id,
         (end_time - start_time) / 1000 as elapsed,
         ROW_NUMBER() OVER ( PARTITION BY service_id ORDER BY (end_time - start_time) )
           AS rn,
         COUNT() OVER ( PARTITION BY service_id ) AS ct
  from   myTable
)
WHERE rn = ROUND( 0.9 * ct );

根据您的业务逻辑,将最后一行更改为使用ROUNDFLOORCEIL。如果我已正确确定逻辑,CEIL将给出与使用PERCENTILE_DISC相同的答案。

  

我需要的是计数是7,删除最后一条记录并返回第6个值(7的90%是0.7,舍入为1),计数是21,删除最后2条记录并返回第19个位置-value(21的90%是2.1轮到2)依此类推。

使用rn = ROUND( 0.9 * ct )

  • 如果计数为7,那么0.9 * 7 = 6.3所以ROUND( 6.3 )会给出第6行
  • 如果计数为21,那么0.9 * 21 = 18.9ROUND( 18.9 )将给出第19行
  • 如果计数为3,那么0.9 * 3 = 2.7ROUND( 2.7 )将给出第3行(最大值)。

目前还不清楚你希望小集返回什么 - 如果你不想要最大行(除了只有一行),那么就像:

WHERE rn = GREATEST( 1, LEAST( ct - 1, ROUND( 0.9 * ct ) ) )