c++ sendinput 给指定的窗口发_数据分析|SQL窗口函数最全使用指南

c++ sendinput 给指定的窗口发_数据分析|SQL窗口函数最全使用指南_第1张图片

d一、why?为什么要使用窗口函数

在日常工作中经常会遇到类似这样的需求:

怎么样得到各部门工资排名前N名的员工列表?
查找各部门每人工资占部门总工资的百分比?

对于这样的需求,使用传统的SQL实现起来比较困难,这类需求都有一个共同的特点,需要在单表中满足某些条件的记录集内部做一些函数操作。不使用窗口函数的话可能要进行多次的表连接操作,可读性差的同时还会影响性能。

二、what?什么是窗口函数

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

什么叫窗口?

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

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

  • 聚合函数是将多条记录聚合为一条;而窗口函数是每条记录都会执行,查询结果并不会改变记录条数,有几条记录执行完还是几条。
  • 普通聚合函数也可以用于窗口函数中,赋予它窗口函数的功能。

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

窗口函数的简单语法如下:

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

举个例子,比如现在有一张学生成绩表student,分别有学生姓名s_name,课程名称c_name和课程成绩score三个字段,有个需求是“给出所有学生每门课的成绩排名”,这个需求可以用序号窗口函数轻松解决,伪代码如下:

<序号窗口函数> over (partition by <课程名称c_name>
                   order by <课程成绩score>)

那么<窗口函数>还有哪些呢?

  1. 一些专用的窗口函数,随后会举例介绍其用法功能
  • 序号函数:row_number() / rank() / dense_rank()
  • 分布函数:percent_rank() / cume_dist()
  • 前后函数:lag() / lead()
  • 头尾函数:first_val() / last_val()
  • 其他函数:nth_value() / 用途:将分区中的有序数据分为n个桶,记录桶号。

2. 原有的聚合函数也可用作窗口函数,如下

  • sum(),avg(),count(),max(),min()

三、how?窗口函数如何使用

这里使用经典的学生-课程-成绩结构,表架构如下:

student(sid, sname, sclass) ——学生表(学生ID,学生姓名,学生班级)
course(cid, cname) ——课程表(课程ID,课程名称)
score(sid, cid, score) ——成绩表 (学生ID,课程ID,成绩)

之后的例子主要是为了介绍函数功能,有些例子可能不太合适哈哈哈哈

  1. 序号函数
  • row_number() 、 rank() 、 dense_rank()都是序号函数,可以通过如下例子了解它们的区别
SELECT s.sname, c.cname, sc.score,
	ROW_NUMBER() OVER (PARTITION BY c.cname
			   ORDER BY sc.score DESC) AS row_num, 
        RANK() OVER (PARTITION BY c.cname
		     ORDER BY sc.score DESC) AS ranking,
        DENSE_RANK() OVER(PARTITION BY c.cname
		          ORDER BY sc.score DESC) AS dense_ranking
FROM student s INNER JOIN score sc ON s.sid = sc.sid
	       INNER JOIN course c ON sc.cid = c.cid	

结果如下:

c++ sendinput 给指定的窗口发_数据分析|SQL窗口函数最全使用指南_第2张图片

可以发现,ROW_NUMBER()函数可以理解为排序号,不考虑并列;RANK()函数也为排号,考虑并列,并列之后的按照实际序号来;dense_rank()同样是排号,考虑并列,并列之后按下一个名次来。

2.分布函数

percent_rank()

  • 用途:和之前的RANK()函数相关,每行按照如下公式进行计算:
(rank - 1) / (rows - 1)

其中,rank为RANK()函数产生的序号,rows为当前窗口的记录总行数。

SELECT s.sname, c.cname, sc.score, 
         PERCENT_RANK() OVER (PARTITION BY c.cname
			      ORDER BY sc.score DESC) as percent																									
FROM student s INNER JOIN score sc ON s.sid = sc.sid
	       INNER JOIN course c ON sc.cid = c.cid

c++ sendinput 给指定的窗口发_数据分析|SQL窗口函数最全使用指南_第3张图片

cume_dist()

  • 用途:分组内大于等于当前rank值的行数/分组内总行数,这个函数比percen_rank使用场景更多。
  • 应用场景:班级中比当前同学成绩高的学生比例是多少
SELECT s.sname, c.cname, sc.score,
       CUME_DIST() OVER (PARTITION BY c.cname
                         ORDER BY sc.score DESC) as cumdist																									
FROM student s INNER JOIN score sc ON s.sid = sc.sid
	       INNER JOIN course c ON sc.cid = c.cid

c++ sendinput 给指定的窗口发_数据分析|SQL窗口函数最全使用指南_第4张图片

其中关羽的北冥神功课考了100分,在这门课程中大于等于100分的比例为10%,也就是说关羽这门课的成绩在班级前10%。

3.前后函数

lead(n) / lag(n)

  • 用途:分组中位于当前行后n行(lead)/ 前n行(lag)的记录值。
  • 应用场景:求每个用户相邻两次浏览的时间差,可以参考我的这篇文章:
狗哥:数据分析|SQL面试题—相邻间隔问题​zhuanlan.zhihu.com
c++ sendinput 给指定的窗口发_数据分析|SQL窗口函数最全使用指南_第5张图片
SELECT s.sname, c.cname, sc.score,
       lead(sc.score,1) OVER (PARTITION BY c.cname
                              ORDER BY sc.score DESC) as leadVal,
       lag(sc.score,1) OVER (PARTITION BY c.cname
                             ORDER BY sc.score DESC) as lagVal
FROM student s INNER JOIN score sc ON s.sid = sc.sid
	       INNER JOIN course c ON sc.cid = c.cid

c++ sendinput 给指定的窗口发_数据分析|SQL窗口函数最全使用指南_第6张图片

4.头尾函数——first_val(expr) / last_val(expr)。

  • 用途:得到分区中的第一个/最后一个指定参数的值。
SELECT s.sname, c.cname, sc.score,
       FIRST_VALUE(sc.score) OVER (PARTITION BY s.sname
                                   ORDER BY sc.score DESC) as firstVal,
       LAST_VALUE(sc.score) OVER (PARTITION BY s.sname
                                  ORDER BY sc.score DESC) as lastVal
FROM student s INNER JOIN score sc ON s.sid = sc.sid
	       INNER JOIN course c ON sc.cid = c.cid

c++ sendinput 给指定的窗口发_数据分析|SQL窗口函数最全使用指南_第7张图片

按姓名分组,截至到‘九阴真经’时,第一个记录为95分,最后一个记录为54分

5.其他函数

nth_value(expr, n)

  • 用途:返回窗口中第N个expr的值,expr可以是表达式,也可以是列名
SELECT s.sname, s.sclass, c.cname, sc.score,
       nth_value(sc.score,1) OVER (PARTITION BY s.sname
                                   ORDER BY sc.score DESC) as 1th,
       nth_value(sc.score,2) OVER (PARTITION BY s.sname
                                   ORDER BY sc.score DESC) as 2th
FROM student s INNER JOIN score sc ON s.sid = sc.sid
               INNER JOIN course c ON sc.cid = c.cid

c++ sendinput 给指定的窗口发_数据分析|SQL窗口函数最全使用指南_第8张图片

nfile()

  • 用途:将分区中的有序数据分为n个桶,记录桶号。
  • 此函数在数据分析中应用较多,比如由于数据量大,需要将数据平均分配到N个并行的进程分别计算,此时就可以用NFILE(N)对数据进行分组,由于记录数不一定被N整除,所以数据不一定完全平均,多出来的部分则依次加给第一组、第二组···直到分配完。

6.聚合函数

  • 用途:在窗口中每条记录动态应用聚合函数(sum/avg/max/min/count),可以动态计算在指定的窗口内的各种聚合函数值。
  • 应用场景:截至到当前课程,每个学生的总成绩/平均成绩/最高成绩/最低成绩/课程数是多少?

由于每个窗口函数的条件相同,所以这里将其起个别名w,每个窗口函数只要调用其别名w就好,如下:

SELECT s.sname, c.cname, sc.score,
    SUM(sc.score) OVER w as sumVal,
    AVG(sc.score) OVER w as avgVal,
    MAX(sc.score) OVER w as maxVal,
    MIN(sc.score) OVER w as minVal,
    COUNT(sc.score) OVER w as countgVal
FROM student s INNER JOIN score sc ON s.sid = sc.sid
	       INNER JOIN course c ON sc.cid = c.cid
WINDOW w as (PARTITION BY s.sname ORDER BY sc.score DESC)

c++ sendinput 给指定的窗口发_数据分析|SQL窗口函数最全使用指南_第9张图片

你可能感兴趣的:(c++,sendinput,给指定的窗口发)