5分钟了解什么是SQL窗口函数

窗口函数的概述

窗口函数 (开窗函数),也叫OLAP函数 (OnlineAnallytical Processing,联机分析处理,可以对数据库据

进行实时分析处理。简单来说,窗口函数指的是over()函数, 它相当于给表新增一列, 至于新增的内容

是什么, 取决于窗口函数, 结合什么函数一起使用.

窗口函数的格式

可以结合窗口函数一起使用的函数 over(partition by 分组字段 order by 排序字段 asc | desc rows

between 起始行 and 结束行)

窗口函数的参数解释

1. partition by类似千聚合函数中的 group by句子,但是在窗口函数中,要写成partition by

2 order by:和普通查询语句中的 ORDER BY没什么不同

窗口函数的分类

聚合函数: count(), sum(), max(), min(), avg()

排序函数: row_number(), rank(), dense_rank(), ntile(数字)

其它函数: lag(), lead(), first_value(), last_value()

细节:

1. 窗口函数, 相当于给表新增一列, 至于新增内容是什么, 取决于窗口函数, 结合什么函数一起使用.

2. 如果不写partition by, 则是全局统计, 如果写了, 则是局部统计(即: 只统计该分组的数据)

3. 如果不写order by, 则统计组内所有的数据, 如果写了, 则统计组内第一行 至 当前行的数据.

4. rows between ... and...可以指定操作行的范围, 包左包右,

涉及到的关键字如下:

        unbounded preceding # 表示: 第1行.

        n preceding # 向上的n行

        current row # 当前行.

        n following # 向下的n行

        unbounded following # 表示: 最后1行.

5. ntile(数字) 表示把数据分成几份, 即: 几分之几, 优先参考最小分区. 例如: 7条数据, 操作之后, 就

是: 1, 1, 1 2, 2 3, 3

以下使用代码实现

数据准备
# 员工表
CREATE TABLE employee
(
    id     int,         # 编号
    name   varchar(20), # 姓名
    deg    varchar(20), # 职位
    salary int,         # 工资
    dept   varchar(20)  # 部门
);

insert into employee value (1201, 'gopal', 'manager', 50000, 'TP');
insert into employee value (1202, 'manisha', 'cto', 50000, 'TP');
insert into employee value (1203, 'khalil', 'dev', 30000, 'AC');
insert into employee value (1204, 'prasanth', 'dev', 30000, 'AC');
insert into employee value (1206, 'kranthi', 'admin', 20000, 'TP');

# 网站点击量表
create table website_pv_info
(
    cookieid   varchar(20), # 用户id
    createtime varchar(20), # 访问时间
    pv         int          # 页面浏览量
);

insert into website_pv_info value ('cookie1', '2018-04-10', 1);
insert into website_pv_info value ('cookie1', '2018-04-11', 5);
insert into website_pv_info value ('cookie1', '2018-04-12', 7);
insert into website_pv_info value ('cookie1', '2018-04-13', 3);
insert into website_pv_info value ('cookie1', '2018-04-14', 2);
insert into website_pv_info value ('cookie1', '2018-04-15', 4);
insert into website_pv_info value ('cookie1', '2018-04-16', 4);
insert into website_pv_info value ('cookie2', '2018-04-10', 2);
insert into website_pv_info value ('cookie2', '2018-04-11', 3);
insert into website_pv_info value ('cookie2', '2018-04-12', 5);
insert into website_pv_info value ('cookie2', '2018-04-13', 6);
insert into website_pv_info value ('cookie2', '2018-04-14', 3);
insert into website_pv_info value ('cookie2', '2018-04-15', 9);
insert into website_pv_info value ('cookie2', '2018-04-16', 7);

# 网站访问记录表
create table website_url_info
(
    cookieid   varchar(20), # 用户id
    createtime varchar(20), # 访问时间
    url        varchar(20)  # 访问的url页面
);
insert into website_url_info value ('cookie1', '2018-04-10 10:00:02', 'url2');
insert into website_url_info value ('cookie1', '2018-04-10 10:00:00', 'url1');
insert into website_url_info value ('cookie1', '2018-04-10 10:03:04', '1url3');
insert into website_url_info value ('cookie1', '2018-04-10 10:50:05', 'url6');
insert into website_url_info value ('cookie1', '2018-04-10 11:00:00', 'url7');
insert into website_url_info value ('cookie1', '2018-04-10 10:10:00', 'url4');
insert into website_url_info value ('cookie1', '2018-04-10 10:50:01', 'url5');
insert into website_url_info value ('cookie2', '2018-04-10 10:00:02', 'url22');
insert into website_url_info value ('cookie2', '2018-04-10 10:00:00', 'url11');
insert into website_url_info value ('cookie2', '2018-04-10 10:03:04', '1url33');
insert into website_url_info value ('cookie2', '2018-04-10 10:50:05', 'url66');
insert into website_url_info value ('cookie2', '2018-04-10 11:00:00', 'url77');
insert into website_url_info value ('cookie2', '2018-04-10 10:10:00', 'url44');
insert into website_url_info value ('cookie2', '2018-04-10 10:50:01', 'url55');
窗口函数初体验
# 案例1: 窗口函数初体验.
# 需求: 统计每个所有员工的工资, 并将其展示在每个员工数据的最后.
# 思路1: 聚合函数直接实现, 能计算出: 所有员工的工资总和, 但是无法达到想要的效果.
select *, sum(salary) as total_salary from employee;

# 思路2: 窗口函数可以实现我们要的效果.
select *, sum(salary) over() as total_salary from employee;
select * from employee;
窗口 + 聚合
# 案例2: 演示 窗口函数 + 聚合函数一起使用.
# 需求:求出网站总的pv数 所有用户所有访问加起来
select *, sum(pv) over() from website_pv_info;                       # 不写partition by, 全局统计(统计表中所有的数据)

# 需求: 求出每个用户总pv数
# 方式1: sum() + group by 一起使用.
select cookieid, sum(pv) as total_pv from website_pv_info group by cookieid;

# 方式2: 聚合函数 + 窗口函数一起使用.
select *, sum(pv) over(partition by cookieid) from website_pv_info;  # 写partition by, 局部统计(只统计该分组的数据.)

# 演示:  如果不写order by, 则统计组内所有的数据, 如果写了, 则统计组内第一行 至 当前行的数据.
select *, sum(pv) over(partition by cookieid order by createtime) from website_pv_info;  # 写order by, 局部统计(统计组内第一行 至 当前行的内容)
# 上述的代码, 等同于:
select
    *,
    sum(pv) over(partition by cookieid order by createtime rows between unbounded preceding and current row ) as n1  # 起始行 到 当前行
from website_pv_info;  # 写order by, 局部统计(统计组内第一行 至 当前行的内容)

# 需求: 统计每个cookieID的pv(访问量), 只统计: 当前行及 向前3行 向后1行
select
    *,
    sum(pv) over(partition by cookieid order by createtime rows between 3 preceding and 1 following)
from website_pv_info;  # 写order by, 局部统计(统计组内第一行 至 当前行的内容)
窗口 + 排序
# 需求: 根据点击量(pv)做排名.   根据cookieid分组, 根据点击量降序排名.
select
    *,
    #                   根据cookieid分组,        根据点击量降序排名.
    row_number() over(partition by cookieid order by pv desc) as rn,       # 例如: 排名为: 1, 2, 3, 4
    rank() over(partition by cookieid order by pv desc) as rk,             # 例如: 排名为: 1, 2, 2, 4
    dense_rank() over(partition by cookieid order by pv desc) as dr       # 例如: 排名为: 1, 2, 2, 3
from website_pv_info;

# ntile(数字,表示分成几份)  采用均分策略, 每份之间的差值不超过1, 优先参考最小的那个部分, 即: 7分成3份, 则是: 3, 2, 2
select
    *,
    #                   根据cookieid分组,        根据点击量降序排名.
    ntile(3) over(partition by cookieid order by pv desc) as nt
from website_pv_info;

# 上述的ntile()是将数据分成指定的 份数, 方便我们取样(采样), 例如: 将数据按组分成5份, 只获取每组的第1份数据, 则计算量的数据量至少降低 5 倍.
# 例如: 上述的数据是分成了3份, 如果我要每组的第一份怎么办?   子查询.
select * from (
    select *,
          #                   根据cookieid分组,        根据点击量降序排名.
          ntile(3) over (partition by cookieid order by pv desc) as nt
    from website_pv_info
) t1 where t1.nt = 1;
窗口 + 其它
# 1. LAG 用于统计窗口内往上第n行值
select
    *,
    # 获取上一行的createtime类的值   根据cookieid分组    根据创建时间排序
    lag(createtime) over(partition by cookieid order by  createtime)
from website_url_info;

# 完整写法如下
select
    *,
    # lag()函数的参数解释: 参1 要操作的列,  参2: 向上的第n行, 参3: 默认值,找不到数据的时候, 用默认值填充.
    lag(createtime, 2, '1970-01-01 00:00:00') over(partition by cookieid order by  createtime)
from website_url_info;

# 2. LEAD 用于统计窗口内往下第n行值
select
    *,
    # LEAD()函数的参数解释: 参1 要操作的列,  参2: 向上的第n行, 参3: 默认值,找不到数据的时候, 用默认值填充.
    LEAD(createtime, 2, '1970-01-01 00:00:00') over(partition by cookieid order by  createtime)
from website_url_info;


# 3. FIRST_VALUE 取分组内排序后,截止到当前行,第一个值
select
    *,
    first_value(createtime) over(partition by cookieid order by  createtime)
from website_url_info;

# 4. LAST_VALUE  取分组内排序后,截止到当前行,最后一个值
select
    *,
    last_value(createtime) over(partition by cookieid order by  createtime)
from website_url_info;

总结

1.窗口函数的功能
        ①同时具有分组 (partition by)和排序 (order by)的功能
        ② 不减少原表的行数
2.窗口函数的执行顾序
        在使用over等开窗函数时,over(里的分组以及排序的执行,晚于 where、group by、 order         by的执行。
3. 窗口函数的使用场景
        ①组内排名
        ②求占比
        ③ TopN问题
4. 注意事项
        ①窗口函数原则上只能写在select子句中。
        ②partition子句原则上可是省略,省略就是 不指定分组。但是,这就失去了窗口函数的功能,所以一般不要这么使用。

你可能感兴趣的:(sql,数据库)