开窗函数

开窗函数

需求:
既要显示聚合前的数据,又要显示聚合后的结果

id name sal 
1   zs  3w
2   ls  2.5w
3   ww  2w  

rank

id  name  sal     rank 
1   zs    3w        1   
2   ls    2.5w      2
3   ww    2w        3

开窗函数:
窗口函数: 窗口 + 函数
窗口: 函数运行时计算的数据集的范围
函数:运行时的函数:
1.常用的聚合函数
2.窗口内置的函数

RANK:从1开始,按照顺序,生成组内记录的编号,排序相同会重复,在名次中留下空位
ROW_NUMBER:从1开始,按照顺序,生成组内记录的编号,序号没有重复的
DENSE_RANK:从1开始,按照顺序,生成组内记录的编号,排序相同会重复,在名次中不留下空位
NTILE(n):把数据按照姓名进行分组 时间排序 结果数据分成 3份数
LAG 向上取第几行
LEAD向下取第几行
FIRST_VALUE(col):取分组后 截止到当前行 第一个值
LAST_VALUE(col):取分组后 截止到当前行 最后一个值

语法结构:
函数 over([partition by xxx,...] [order by xxx,...])
over: 以谁进行开窗 =》 table
partition by:以谁进行分组(类似group by ) =》 字段
order by:以谁进行排序 =》字段

1.聚合函数 -》开窗

数据:

zuoshao,2022-03-10,1
zuoshao,2022-03-11,2
zuoshao,2022-03-12,7
zuoshao,2022-03-13,3
zuoshao,2022-03-14,2
zuoshao,2022-03-15,4
zuoshao,2022-03-16,4

create table zuoshao_window(
    name varchar(255),
    dt varchar(20),
    cnt int
);

需求:
统计累计的问题,每个用户每天累计点外卖的次数

name    dt      cnt  sum_cnt
zuoshao,2022-03-10,1,1
zuoshao,2022-03-11,2,3
zuoshao,2022-03-12,7,10
zuoshao,2022-03-13,3,13
zuoshao,2022-03-14,2,15
zuoshao,2022-03-15,4,19
zuoshao,2022-03-16,4,23

函数 over([partition by xxx,...] [order by xxx,...])

2.指定窗口大小

select 
name,
dt,
cnt,
sum(cnt) over(partition by name order by dt) as sum_cnt,
//无上限到当前行
sum(cnt) over(partition by name order by dt rows between unbounded preceding and current row  ) as sum_cnt1,
//不排序 所有
sum(cnt) over(partition by name ) as sum_cnt2,
//当前行的前3行到当前行
sum(cnt) over(partition by name order by dt rows between 3 preceding and current row) as sum_cnt3,
//当前行的前3行到后一行
sum(cnt) over(partition by name order by dt rows between 3 preceding and 1 following) as sum_cnt4,
当前行到底行
sum(cnt) over(partition by name order by dt rows between current row  and unbounded following) as sum_cnt5
from zuoshao_window;
select 
name,
dt,
cnt,
sum(cnt) over(partition by name order by dt) as sum_cnt,
sum(cnt) over(partition by name ) as sum_cnt2,
sum(cnt) over(order by dt ) as sum_cnt3,
sum(cnt) over( ) as sum_cnt4
from zuoshao_window 
order by  dt,name ;
+---------+------------+------+---------+----------+----------+----------+
| name    | dt         | cnt  | sum_cnt | sum_cnt2 | sum_cnt3 | sum_cnt4 |
+---------+------------+------+---------+----------+----------+----------+
| zuoshao | 2022-03-10 |    1 |       1 |       23 |        1 |       23 |
| zuoshao | 2022-03-11 |    2 |       3 |       23 |        3 |       23 |
| zuoshao | 2022-03-12 |    7 |      10 |       23 |       10 |       23 |
| zuoshao | 2022-03-13 |    3 |      13 |       23 |       13 |       23 |
| zuoshao | 2022-03-14 |    2 |      15 |       23 |       15 |       23 |
| zuoshao | 2022-03-15 |    4 |      19 |       23 |       19 |       23 |
| zuoshao | 2022-03-16 |    4 |      23 |       23 |       23 |       23 |
+---------+------------+------+---------+----------+----------+----------+

3.开窗 -内置函数
RANK
ROW_NUMBER
DENSE_RANK
NTILE
1.NTILE
需求:
把数据按照姓名进行分组 时间排序 结果数据分成 3份数

select 
name,
dt,
cnt,
NTILE(3) over(partition by name order by dt) as n1,
NTILE(2) over(partition by name order by dt) as n2
from zuoshao_window 
order by  name ;
+---------+------------+------+------+------+
| name    | dt         | cnt  | n1   | n2   |
+---------+------------+------+------+------+
| zuoshao | 2022-03-10 |    1 |    1 |    1 |
| zuoshao | 2022-03-11 |    2 |    1 |    1 |
| zuoshao | 2022-03-12 |    7 |    1 |    1 |
| zuoshao | 2022-03-13 |    3 |    2 |    1 |
| zuoshao | 2022-03-14 |    2 |    2 |    2 |
| zuoshao | 2022-03-15 |    4 |    3 |    2 |
| zuoshao | 2022-03-16 |    4 |    3 |    2 |
+---------+------------+------+------+------+

NTILE(N):
把数据平均分配到N中,如果不能平均分配,优先分配到较小的编号中。
2.rank相关的
RANK
ROW_NUMBER
DENSE_RANK

select 
name,
dt,
cnt,
sum(cnt) over(partition by name order by dt) as sum_cnt,
rank() over(partition by name order by cnt) as rk,
row_number() over(partition by name order by cnt) as rn,
dense_rank() over(partition by name order by cnt) as d_rk
from zuoshao_window 
order by  name ;
+---------+------------+------+---------+----+----+------+
| name    | dt         | cnt  | sum_cnt | rk | rn | d_rk |
+---------+------------+------+---------+----+----+------+
| zuoshao | 2022-03-10 |    1 |       1 |  1 |  1 |    1 |
| zuoshao | 2022-03-11 |    2 |       3 |  2 |  2 |    2 |
| zuoshao | 2022-03-14 |    2 |      15 |  2 |  3 |    2 |
| zuoshao | 2022-03-13 |    3 |      13 |  4 |  4 |    3 |
| zuoshao | 2022-03-15 |    4 |      19 |  5 |  5 |    4 |
| zuoshao | 2022-03-16 |    4 |      23 |  5 |  6 |    4 |
| zuoshao | 2022-03-12 |    7 |      10 |  7 |  7 |    5 |
+---------+------------+------+---------+----+----+------+

RANK:
从1开始,按照顺序,生成组内记录的编号,排序相同会重复,在名次中留下空位
ROW_NUMBER:
从1开始,按照顺序,生成组内记录的编号,序号没有重复的
DENSE_RANK:
从1开始,按照顺序,生成组内记录的编号,排序相同会重复,在名次中不留下空位
3.蹿行问题
lag 向上取第几行
lead 向下取第几行

  • lag(input[, offset[, default]])
    input =》 col
    offset =》 取第几行
    default=》 取不到时 给一个默认值
select 
name,
dt,
cnt,
sum(cnt) over(partition by name order by dt) as sum_cnt,
lag(dt,1,"9999-99-99") over(partition by name order by dt) as lag_v,
lead(dt,1,"9999-99-99") over(partition by name order by dt) as lead_v
from zuoshao_window 
order by  name ;

4.取值问题
FIRST_VALUE(col):取分组后 截止到当前行 第一个值
LAST_VALUE(col):取分组后 截止到当前行 最后一个值

select 
name,
dt,
cnt,
sum(cnt) over(partition by name order by dt) as sum_cnt,
first_value(dt) over(partition by name order by dt) as first_v,
last_value(dt) over(partition by name order by dt) as last_v
from zuoshao_window 
order by  name ;

开窗函数案例:
1.我们有如下的用户访问数据
userId visitDate visitCount
u01 2017/1/21 5
u02 2017/1/23 6
u03 2017/1/22 8
u04 2017/1/20 3
u01 2017/1/23 6
u01 2017/2/21 8
U02 2017/1/23 6
U01 2017/2/22 4
要求使用SQL统计出每个用户的累积访问次数,如下表所示:
用户id 月份 小计 累积
u01 2017-01 11 11
u01 2017-02 12 23
u02 2017-01 12 12
u03 2017-01 8 8
u04 2017-01 3 3

每个用户的累积访问次数=》
每个用户每个月累计访问次数
维度: 用户、月
指标:次数、累计访问次数
1.etl:
2017/2/22 =》 2017-02 日期函数 ,string函数 sql里面
2017/2/22=>2017-2-22
2.
1.先求 每个月 次数
2. 1结果 =》 累计

//使用date_format实现
        select 
        -- 每个用户每个月 累计次数
        userid,
        month,
        cnt,
        sum(cnt) over(partition by userid order by month) as sum_cnt
        from 
        (
            select  -- 每个用户每个月 次数 
            userid,
            date_format(replace(visitdate,'/','-'), '%Y-%m') as month ,
            sum(visitcount) as cnt
            from test1
            group by 
            userid,month
        ) a;

也可以使用str_to_date
date_format(str_to_date(visitdate,'%Y/%m/%d') ,'%Y-%m')as month

//使用substr实现
        select 
        -- 每个用户每个月 累计次数
        userid,
        month,
        cnt,
        sum(cnt) over(partition by userid order by month) as sum_cnt
        from 
        (
            select  -- 每个用户每个月 次数 
            userid,
            substr(replace(visitdate,'/','-'),1,6) as month,
            sum(visitcount) as cnt
            from test1
            group by 
            userid,month
        ) a;

2.有50W个京东店铺,每个顾客访客访问任何一个店铺的任何一个商品时都会产生一条访问日志,
访问日志存储的表名为Visit,访客的用户id为user_id,被访问的店铺名称为shop,数据如下:
u1 a
u2 b
u1 b
u1 a
u3 c
u4 b
u1 a
u2 c
u5 b
u4 b
u6 c
u2 c
u1 b
u2 a
u2 a
u3 a
u5 a
u5 a
u5 a
请统计:
(1)每个店铺的UV(访客数) 、pv(访问量)
维度:店铺
指标:uv =》user_id

//使用distinct
        select 
        shop,
        count(distinct user_id) as uv ,
        count(user_id) as pv 
        from test2
        group by 
        shop;
//使用union all
//最后的字段shop、uv、pv
        -- 合并表
        select 
        shop,
        sum(uv) uv ,
        sum(pv) pv
        from 
        (
            select 
            shop,
            0 as uv ,
            count(1) as pv
            from test2
            group by 
            shop    

            union all 
            select 
            shop,
            count(1) as uv ,
            0 as pv 
            from (
                select 
                shop,
                user_id
                from test2
                group by 
                shop,
                user_id
            ) a 
            group by 
            shop
        ) a 
        group by
        shop;
//先算每个shop每个人的pv
select
shop,
count(1) as user_uv,
sum(pv) as user_pv
from
(
    select
    user_id,
    shop,
    count(user_id) as pv
    from test2
    group by
    shop,user_id
) tmp
group by
shop;
        

(2)每个店铺访问次数top3的访客信息。输出店铺名称、访客id、访问次数
维度:店铺、 访客id
指标:访问次数 、访问次数的top3

        select
        shop,
        user_id,
        cnt,
        rk
        from 
        (
            select 
            shop,
            user_id,
            cnt,
            row_number() over (partition by shop  order by cnt desc)  as rk
            from 
            (
                select 
                shop,
                user_id,
                count(1) as cnt 
                from test2
                group by 
                shop,
                user_id
            ) a 
        ) a
        where 
        rk <=3;

uv pv :
pv => page 次数 不需要去重
uv => user 次数 需要去重
2.行转列 &&& 列转行
1.列转行

create table t1(
    name varchar(255),
    hobby varchar(255)
);

insert into t1 VALUES("zuoshao","王者荣耀");
insert into t1 VALUES("zuoshao","黑丝");
insert into t1 VALUES("zuoshao","看小视频");
insert into t1 VALUES("xuanxuan","姐姐");
insert into t1 VALUES("xuanxuan","天天");
insert into t1 VALUES("xuanxuan","杰伦");

||
v
zuoshao,<王者荣耀,黑丝,看小视频>
xuanxuan,<姐姐,天天,杰伦>
mysql没有collection_list(hobby)
可以使用group_concat(hobby)

select 
name,
group_concat(hobby) as hobbyies
from t1
group by 
name;

-- hive
select
name,
concat_ws(",",collection_list(hobby)) as hobbyies
from t1
group by
name;

concat_ws
concat

在字符串之间加个分隔符
SELECT CONCAT_WS("-", "江西省", "赣州市", "于都县");
SELECT CONCAT_WS("-", "江西省", NULL, "于都县");
SELECT CONCAT_WS("-", NULL, "赣州市", "于都县");
如果分隔符是NULL,则结果为NULL
SELECT CONCAT_WS(NULL, "江西省", "赣州市", "于都县");
直接拼接起来
SELECT CONCAT("-", "江西省", "赣州市", "于都县");

2.行转列
hive爆破函数实现

select 
name,
hobby_t
from hobby 
lateral view expolde( split(hobbyies,",") ) hobby_tmp as hobby_t;

mysql没有爆破函数(免费版本没有)

split   substring_index(col,",",num)

select substring_index("1,2,3,4",",",1)

mysql> select substring_index("1,2,3,4",",",1);
+----------------------------------+
| substring_index("1,2,3,4",",",1) |
+----------------------------------+
| 1                                |
+----------------------------------+
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 215,463评论 6 497
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,868评论 3 391
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 161,213评论 0 351
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,666评论 1 290
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,759评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,725评论 1 294
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,716评论 3 415
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,484评论 0 270
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,928评论 1 307
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,233评论 2 331
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,393评论 1 345
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,073评论 5 340
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,718评论 3 324
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,308评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,538评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,338评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,260评论 2 352

推荐阅读更多精彩内容

  • 一、应用场景: 用于分区排序 动态Group By top N 累计计算 二、函数介绍 1、窗口函数: first...
    米小河123阅读 1,305评论 0 1
  • over在聚合函数中的使用:一般格式:聚合函数名(列) over(选项)over必须与聚合函数或排序函数一起使用...
    酸甜柠檬26阅读 5,405评论 0 5
  • 1. 介绍 普通聚合函数聚合的行集是组,开窗函数聚合的行集是窗口。因此,普通聚合函数每组(Group by)只有一...
    Yobhel阅读 559评论 0 2
  • 1. 介绍 普通聚合函数聚合的行集是组,开窗函数聚合的行集是窗口。因此,普通聚合函数每组(Group by)只有一...
    幸运猪x阅读 8,158评论 0 4
  • 一.原始数据 执行如下函数后分别得到不同的结果,以此来理解开窗函数的使用方法 二.原始数据 相关函数说明OVER(...
    香山上的麻雀阅读 2,426评论 2 5