HQL中窗口函数用法举例

1、序号函数:row_number() / rank() / dense_rank()

        RANK():并列排序,跳过重复序号 —— 1、1、3

        ROW_NUMBER():顺序排序 —— 1、2、3

        DENSE_RANK(): 并列排序,不跳过重复序号 —— 1、1、2

--给窗口指定别名:WINDOW my_window_name AS (PARTITION BY uid ORDER BY score)
SELECT
    uid,
    score,
    rank() OVER my_window_name AS rk_num,
    row_number() OVER my_window_name AS row_num
FROM exam_record
WINDOW my_window_name AS (PARTITION BY uid ORDER BY score)

附注:也可以使用自连接的方法实现分数排序,相当于rank()

SELECT 
    P1.uid,
    P1.score,
    (SELECT 
        COUNT(P2.score)
    FROM exam_record P2 
    WHERE P2.score > P1.score) + 1 AS rank_1
FROM exam_record P1
ORDER BY rank_1;

 

这里1234447..,如果想要7改为5呢,不跳过位次。相当于DENSE_RANK 函数。

只需要改 COUNT(P2.score) 为 COUNT(distinct P2.score) 即可。

2、分布函数:percent_rank()/cume_dist() 

1)percent_rank():

        将某个数值在数据集中的排位作为数据集的百分比值返回,此处的百分比值得范围为0到1.此函数可用于计算值在数据集内的相对位置。如班级成绩为例,返回的百分数30%表示某个分数在班级总分排名的前30%。

每行按照公式(rank-1)/(rows-1)进行计算。其中rank为RANK()函数产生的序号,rows为当前窗口的记录总行数。

SELECT
    uid,
    score,
    rank() OVER my_window_name AS rank_num,
    PERCENT_RANK() OVER my_window_name AS prk
FROM exam_record
WINDOW my_window_name AS (ORDER BY score desc)

2)cume_dist():

如果按升序排列,则统计:小于等于当前值的行数/总行数。

如果按降序排列,则统计:大于等于当前值的行数/总行数。

SELECT
    uid,
    score,
    rank() OVER my_window_name AS rank_num,
    cume_dist() OVER my_window_name AS cume_dist_num
FROM exam_record
WINDOW my_window_name AS (ORDER BY score asc)

3、前后函数:lag(expr,n,defval)/lead(expr,n,defval) ※

        Lag()和Lead() 分析函数可以在同一次查询中取出同一字段的前N行的数据(Lag)和后N行的数据(Lead)作为独立的列。

常用来取今天和昨天某个字段的插值,当然这种操作也可以用表自连接实现,但是Lag()和Lead()效率更高。函数语法如下:

lag( exp_str,offset,defval) over(partition by .. order by …)
 
lead(exp_str,offset,defval) over(partition by .. order by …)

其中:

  • exp_str 是字段名

  • Offset 是偏移量,即是上1个或上N个的值,假设当前行在表中排在第5行,offset 为3,则表示我们所要找的数据行就是表中的第2行(即5-3=2)。

  • Defval 默认值,当两个函数取 上N 或者 下N 个值,当在表中从当前行位置向前数N行已经超出了表的范围时,lag() 函数将defval这个参数值作为函数的返回值,若没有指定默认值,则返回NULL,那么在数学运算中,总要给一个默认值才不会出错。

  • 返回位于当前行的前 n 行的expr的值:LAG(expr,n)

  • 返回位于当前行的后n行的expr的值:LEAD(expr,n)

举例:查询前1名同学及后一名同学的成绩和当前同学成绩的差值(只排分数,不按uid分组)、

1、先将前一名和后一名同学的分数与当前行的分数放在一起。

SELECT
    uid,
    score,
    LAG(score,1,0) OVER my_window_name AS `前一名分数`,
    LEAD(score,1,0) OVER my_window_name AS `后一名分数`
FROM exam_record
WINDOW my_window_name AS (ORDER BY score desc)

SELECT
    uid,
    score,
    score - `前一名分数` AS `与前一名分差`,
    score - `后一名分数` AS `与后一名分差`
FROM (
    SELECT
        uid,
        score,
        LAG(score,1,0) OVER my_window_name AS `前一名分数`,
        LEAD(score,1,0) OVER my_window_name AS `后一名分数`
    FROM exam_record
    WINDOW my_window_name AS (ORDER BY score desc)
) res

 

4、头尾函数:FIRST_VALUE(expr),LAST_VALUE(expr)

  • FIRST_VALUE(expr),返回第一个expr的值

  • LAST_VALUE(expr),返回最后一个expr的值

  • 应用场景:截止到当前成绩,按照分数排序查询第1个和最后1个的分数

SELECT
        uid,
        score,
        FIRST_VALUE(score) OVER my_window_name AS `第一行分数`,
        LAST_VALUE(score) OVER my_window_name AS `最后一行分数`
    FROM exam_record
    WINDOW my_window_name AS (ORDER BY score desc)

5、聚合函数+窗口函数联合使用

聚合函数也可以用于窗口函数,因窗口函数的执行顺序(逻辑上的)是在FROM,JOIN,WHERE,GROUP BY,HAVING之后,在ORDER BY,LIMIT,SELECT DISTINCE之前,它执行时GROUP BY的聚合过程已经完成了,所以不会再产生数据聚合。

注:窗口函数是在where之后执行的,所以如果where子句需要用窗口函数作为条件,需要多一层查询,在子查询外面进行。

SELECT
        uid,
        score,
        sum(score) OVER my_window_name AS sum_score,
        max(score) OVER my_window_name AS max_score,
        min(score) OVER my_window_name AS min_score,
        avg(score) OVER my_window_name AS avg_score
    FROM exam_record
    WINDOW my_window_name AS (ORDER BY score desc)

 

mysql> SELECT 
    ->     stu_id, 
    ->     lesson_id, 
    ->     score, 
    ->     create_time,
    ->     FIRST_VALUE(score) OVER w AS first_score, -- 按照lesson_id分区,create_time升序,取第一个score值
    ->     LAST_VALUE(score) OVER w AS last_score -- 按照lesson_id分区,create_time升序,取最后一个score值
    -> FROM t_score
    -> WHERE lesson_id IN ('L001','L002')
    -> WINDOW w AS (PARTITION BY lesson_id ORDER BY create_time)
    -> ;
+--------+-----------+-------+-------------+-------------+------------+
| stu_id | lesson_id | score | create_time | first_score | last_score |
+--------+-----------+-------+-------------+-------------+------------+
|      3 | L001      |   100 | 2018-08-07  |         100 |        100 |
|      1 | L001      |    98 | 2018-08-08  |         100 |         98 |
|      2 | L001      |    84 | 2018-08-09  |         100 |         99 |
|      4 | L001      |    99 | 2018-08-09  |         100 |         99 |
|      3 | L002      |    91 | 2018-08-07  |          91 |         91 |
|      1 | L002      |    86 | 2018-08-08  |          91 |         86 |
|      2 | L002      |    90 | 2018-08-09  |          91 |         90 |
|      4 | L002      |    88 | 2018-08-10  |          91 |         88 |
+--------+-----------+-------+-------------+-------------+------------+
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值