MySQL使用聚类窗口函数求P90

1,232 阅读3分钟

什么是窗口函数

窗口函数也称为OLAP(Online Anallytical Processing)函数,意思是对数据库数据进行实时分析处理。窗口函数就是为了实现OLAP而添加的标准SQL功能。

窗口的概念非常重要,它可以理解为记录集合,窗口函数也就是在满足某种条件的记录集合上执行的特殊函数,对于每条记录都要在此窗口内执行函数,有的函数,随着记录不同,窗口大小都是固定的,这种属于静态窗口;有的函数则相反,不同的记录对应着不同的窗口,这种动态变化的窗口叫滑动窗口。

窗口函数和普通聚合函数也很容易混淆,二者区别如下:

聚合函数是将多条记录聚合为一条;而窗口函数是每条记录都会执行,查询结果并不会改变记录条数,有几条记录执行完还是几条。 普通聚合函数也可以用于窗口函数中,赋予它窗口函数的功能。 原因就在于窗口函数的执行顺序(逻辑上的)是在FROM,JOIN,WHERE,GROUP BY,HAVING之后,在ORDER BY,LIMIT,SELECT DISTINCT之前。它执行时GROUP BY的聚合过程已经完成了,所以不会再产生数据聚合。

窗口函数的简单语法如下

<窗口函数> OVER (partition by <用于分组的列名> order by <用于排序的列名>)

聚类窗口函数

聚和窗口函数和上面提到的专用窗口函数用法完全相同,只需要把聚合函数写在窗口函数的位置即可,但是函数后面括号里面不能为空,需要指定聚合的列名

select *,
   sum(成绩) over (order by 学号) as current_sum,
   avg(成绩) over (order by 学号) as current_avg,
   count(成绩) over (order by 学号) as current_count,
   max(成绩) over (order by 学号) as current_max,
   min(成绩) over (order by 学号) as current_min
from 班级表

聚合函数作为窗口函数,可以在每一行的数据里直观的看到,截止到本行数据,统计数据是多少(最大值、最小值等)。同时可以看出每一行数据,对整体统计数据的影响。

使用聚类窗口函数求P90

对实时统计数据二次加工,根据页面分组计算一天前 白屏、首屏的最大值、平均值、P90

先计算白屏、首屏的最大值、平均值

SELECT  
ROUND( t1.spv / t2.totalPv * 100, 2 ) AS 'pv占比',  
ROUND( t1.suv / t2.totalUv * 100, 2 ) AS 'uv占比',  
t1.`uri` AS '页面地址',  
t1.maxFirstPaint AS '白屏最大值',  
ROUND( t1.avgFirstPaint, 2 ) AS '白屏平均值',  
t1.maxFirstScreen AS '首屏最大值',  
ROUND( t1.avgFirstScreen, 2 ) AS '首屏平均值'
FROM  
(  
SELECT COALESCE  
( page_params_alias, '总数' ) 'uri',  
sum( pv ) AS spv,  
sum( uv ) AS suv,  
max( first_paint ) AS maxFirstPaint,  
avg( first_paint ) AS avgFirstPaint,  
max( first_screen ) AS maxFirstScreen,  
avg( first_screen ) AS avgFirstScreen
FROM  
tt_ty_overview_ux  
WHERE  
TO_DAYS( NOW( ) ) - TO_DAYS( freshen_time ) <= 1  
GROUP BY  
page_params_alias  
) t1 ,  
(  
SELECT  
sum( pv ) AS totalPv,  
sum( uv ) AS totalUv  
FROM  
tt_ty_overview_ux  
WHERE  
TO_DAYS( NOW( ) ) - TO_DAYS( freshen_time ) <= 1  
AND locate( 'oss', HOST ) = 0  
) t2

image.png

白屏、首屏的P90计算逻辑

  1. 根据页面地址找出时间范围内的数据,分组排序
select * from (  
SELECT  
js_error_rate,  
page_params_alias,  
ROW_NUMBER() OVER ( PARTITION BY page_params_alias ORDER BY js_error_rate ASC ) AS p90  
FROM  
tt_ty_overview_ux  
WHERE  
TO_DAYS( NOW( ) ) - TO_DAYS( freshen_time ) <= 1  
  
) t  

image.png

PARTITION BY子句将结果集划分为分区。 ROW_NUMBER()函数分别应用于每个分区,并重新初始化每个分区的行号。PARTITION BY子句是可选的。如果未指定,ROW_NUMBER()函数会将整个结果集视为单个分区。

· ORDER BY子句定义结果集的每个分区中的行的逻辑顺序。 ORDER BY子句是必需的,因为ROW_NUMBER()函数对顺序敏感。

· OVER()是一个开窗函数,对集合进行聚合计算

  1. 得到分组总数 * 0.9
select * from (  
  
SELECT  
js_error_rate,  
page_params_alias,  
count(1) over (PARTITION BY page_params_alias) as cn,  
ROW_NUMBER() OVER ( PARTITION BY page_params_alias ORDER BY js_error_rate ASC ) AS p90  
FROM  
tt_ty_overview_ux  
WHERE  
TO_DAYS( NOW( ) ) - TO_DAYS( freshen_time ) <= 1  
AND locate( 'oss', HOST ) = 0  
  
) t  
WHERE  
t.p90 = ROUND( t.cn * 0.9 )

image.png

对比cn和 p90,在分组总行数百分之九十的位置,不整除的话四舍五入。

  1. 整合最大值、平均值、P90即可。

参考资料 : mysql窗口函数