Hive 1.2.1 窗口函数

1. 借鉴

网名在线生成器
HIVE over() 超全讲解
Hive分析窗口函数(一) SUM,AVG,MIN,MAX

2. 开始

数据准备

db_company.hotel_order 数据下载
表结构如下:

create external table db_company.hotel_order
(
  id bigint,
  name string,
  order_date string,
  price bigint,
  code string
)
row format delimited
fields terminated by ',';

加载数据

load data local inpath '/opt/envs/datas/hotel/hotel_order.txt' into table db_company.hotel_order;

窗口函数

  • OVER()
    指定分析函数工作的数据窗口大小
    注意点:
    ① over前面必须是指定的函数
    ② 括号里面可以写内容,限定窗口大小,如果不写,表示对查询出来的所有数据集进行开窗
    ③ 为每一组数据都进行开窗

那么括号里可以写那些内容呢?

内容 释义
CURRENT ROW 表示当前行
n PRECEDING 表示往前n行数据
n FOLLOWING 表示往后n行数据
UNBOUNDED PRECEDING 表示从前面的起点
UNBOUNDED FOLLOWING 表示到后面的终点

那个又有哪些指定的函数呢?

内容 释义
MAX(col)                                                     取最大值
MIN(col) 取最小值
AVG(col) 取平均值
COUNT(col) 取总数
LAG(col,n) 往前第n行数据,第三个参数为默认值,可选
LEAD(col,n) 往后第n行数据,第三个参数为默认值,可选
NTILE(n) 把有序分区中的行分发到指定数据的组中,各个组有编号,编号从1开始,对于每一行,NTILE返回此行所属的组的编号。注意:n必须为int类型。
RANK() 排序相同时会重复,总数不会变(并列)
[4个人,排名为:1,1,3,4]
DENSE_RANK() 排序相同时会重复,总数会减少
[4个人,排名为:1,1,2,3]
ROW_NUMBER() 会根据顺序计算
[4个人,排名为:1,2,3,4]

栗子

我们通过一些栗子来看下窗口函数

  • ① 查询2020-06,每个人入住总数以及总入住人数

    select name, count(*), count(*) over()
    from db_company.hotel_order
    where date_format(order_date, 'yyyy-MM') = '2020-06' group by name;
    

    结果如下:

    name    _c1     count_window_0
    齐钰    2       6
    酒博瀚  3       6
    碧千易  2       6
    源采文  2       6
    掌蓉城  2       6
    丰雅丽  4       6
    

    我们分析一下,因为我们使用了count()和count() over()。看样子后面就多了一个over()为啥结果前面和后面不一样呢?group by之后,count()表示统计各个分组之后的数量,而count() over()则是对统计分组的数量(因为over里面我们没有写内容,且over前面的聚合函数式count)。

  • ② 查询用户的入住明细以及所有用户的入住总金额

    select *, sum(price) over()
    from db_company.hotel_order;
    

    结果如下:

    hotel_order.id  hotel_order.name        hotel_order.order_date  hotel_order.price       hotel_order.code        sum_window_0
    6       源采文  2020-06-02      103     00174553        1461
    6       源采文  2020-06-01      103     00174553        1461
    5       掌蓉城  2020-06-02      103     01341433        1461
    5       掌蓉城  2020-06-01      103     01341433        1461
    4       酒博瀚  2020-06-04      101     00277553        1461
    4       酒博瀚  2020-06-03      101     00277553        1461
    4       酒博瀚  2020-06-02      101     00277553        1461
    3       齐钰    2020-06-02      101     02371493        1461
    3       齐钰    2020-06-01      101     02371493        1461
    2       碧千易  2020-06-13      93      02371493        1461
    2       碧千易  2020-06-13      93      02371493        1461
    1       丰雅丽  2020-06-13      91      02371493        1461
    1       丰雅丽  2020-06-03      89      10271563        1461
    1       丰雅丽  2020-06-02      89      10271563        1461
    1       丰雅丽  2020-06-01      89      10271563        1461
    

    可以看到over() + 前面的聚合函数,不一定非得跟着group by,但是它的概念还是包含了组的。

  • ③ 根据时间排序,将price进行累加

    select *, sum(price) over(order by order_date rows between UNBOUNDED PRECEDING and CURRENT ROW) 
    from db_company.hotel_order;
    

    结果如下:

    hotel_order.id  hotel_order.name        hotel_order.order_date  hotel_order.price       hotel_order.code        sum_window_0
    3       齐钰    2020-06-01      101     02371493        101
    1       丰雅丽  2020-06-01      89      10271563        190
    6       源采文  2020-06-01      103     00174553        293
    5       掌蓉城  2020-06-01      103     01341433        396
    6       源采文  2020-06-02      103     00174553        499
    5       掌蓉城  2020-06-02      103     01341433        602
    4       酒博瀚  2020-06-02      101     00277553        703
    1       丰雅丽  2020-06-02      89      10271563        792
    3       齐钰    2020-06-02      101     02371493        893
    1       丰雅丽  2020-06-03      89      10271563        982
    4       酒博瀚  2020-06-03      101     00277553        1083
    4       酒博瀚  2020-06-04      101     00277553        1184
    2       碧千易  2020-06-13      93      02371493        1277
    2       碧千易  2020-06-13      93      02371493        1370
    1       丰雅丽  2020-06-13      91      02371493        1461
    

    另外我还发现以下两种写法,虽然这两种写法跟我们的需求不符,但是通过对以下这两种方式的观察我们发现是先执行over函数,再执行order排序,所以以后使用over函数时需要注意这一点。
    第一种:排序写在over外面。

    select *, sum(price) over(rows between UNBOUNDED PRECEDING and CURRENT ROW) 
    from db_company.hotel_order
    order by order_date;
    

    这种方式的结果如下:

    hotel_order.id  hotel_order.name        hotel_order.order_date  hotel_order.price       hotel_order.code        sum_window_0
    1       丰雅丽  2020-06-01      89      10271563        1461
    3       齐钰    2020-06-01      101     02371493        917
    6       源采文  2020-06-01      103     00174553        206
    5       掌蓉城  2020-06-01      103     01341433        412
    3       齐钰    2020-06-02      101     02371493        816
    1       丰雅丽  2020-06-02      89      10271563        1372
    4       酒博瀚  2020-06-02      101     00277553        715
    5       掌蓉城  2020-06-02      103     01341433        309
    6       源采文  2020-06-02      103     00174553        103
    4       酒博瀚  2020-06-03      101     00277553        614
    1       丰雅丽  2020-06-03      89      10271563        1283
    4       酒博瀚  2020-06-04      101     00277553        513
    1       丰雅丽  2020-06-13      91      02371493        1194
    2       碧千易  2020-06-13      93      02371493        1010
    2       碧千易  2020-06-13      93      02371493        1103
    

    第二种,不根据order_date进行排序

    select *, sum(price) over( rows between UNBOUNDED PRECEDING and current row) 
    from db_company.hotel_order;
    

    结果如下:

    hotel_order.id  hotel_order.name        hotel_order.order_date  hotel_order.price       hotel_order.code        sum_window_0
    6       源采文  2020-06-02      103     00174553        103
    6       源采文  2020-06-01      103     00174553        206
    5       掌蓉城  2020-06-02      103     01341433        309
    5       掌蓉城  2020-06-01      103     01341433        412
    4       酒博瀚  2020-06-04      101     00277553        513
    4       酒博瀚  2020-06-03      101     00277553        614
    4       酒博瀚  2020-06-02      101     00277553        715
    3       齐钰    2020-06-02      101     02371493        816
    3       齐钰    2020-06-01      101     02371493        917
    2       碧千易  2020-06-13      93      02371493        1010
    2       碧千易  2020-06-13      93      02371493        1103
    1       丰雅丽  2020-06-13      91      02371493        1194
    1       丰雅丽  2020-06-03      89      10271563        1283
    1       丰雅丽  2020-06-02      89      10271563        1372
    1       丰雅丽  2020-06-01      89      10271563        1461
    

    拓展:解释一下这个HQL
    我们看下下面这个HQL

    select *, sum(price) over(order by order_date) 
    from db_company.hotel_order;
    

    结果如下:

    hotel_order.id  hotel_order.name        hotel_order.order_date  hotel_order.price       hotel_order.code        sum_window_0
    3       齐钰    2020-06-01      101     02371493        396
    1       丰雅丽  2020-06-01      89      10271563        396
    6       源采文  2020-06-01      103     00174553        396
    5       掌蓉城  2020-06-01      103     01341433        396
    6       源采文  2020-06-02      103     00174553        893
    5       掌蓉城  2020-06-02      103     01341433        893
    4       酒博瀚  2020-06-02      101     00277553        893
    1       丰雅丽  2020-06-02      89      10271563        893
    3       齐钰    2020-06-02      101     02371493        893
    1       丰雅丽  2020-06-03      89      10271563        1083
    4       酒博瀚  2020-06-03      101     00277553        1083
    4       酒博瀚  2020-06-04      101     00277553        1184
    2       碧千易  2020-06-13      93      02371493        1461
    2       碧千易  2020-06-13      93      02371493        1461
    1       丰雅丽  2020-06-13      91      02371493        1461
    

    我们上面说了,over不一定跟着group by,但是它的概念包含了组,就是说这个组里面可以一条也可以多条数据,也可以说每条成组或者多条成组。我们按照order_date进行排序,我们也说了它会为每一组数据开窗,啥叫每一组?我们按照order_date排序, 第一组的2020-06-01日期都分到了一组。如果你要问为什么?那有没有比它小的?没有。有没有比它大的?有,比它大的分到它们对应的组。那它是不是分为一组?是,所以[2020-06-01]分为一组。

    所以总共分为以下组
    [2020-06-01],
    [2020-06-01,2020-06-02],
    [2020-06-01,2020-06-02,2020-06-03],
    [2020-06-01,2020-06-02,2020-06-03,2020-06-04],
    [2020-06-01,2020-06-02,2020-06-03,2020-06-04,2020-06-13]
    然后对组内进行sum(price)计算

  • ④ 计算每个月的总金额

    select *, sum(price) over(distribute by order_date) 
    from db_company.hotel_order;
    

    结果如下:

    hotel_order.id  hotel_order.name        hotel_order.order_date  hotel_order.price       hotel_order.code        sum_window_0
    3       齐钰    2020-06-01      101     02371493        396
    1       丰雅丽  2020-06-01      89      10271563        396
    6       源采文  2020-06-01      103     00174553        396
    5       掌蓉城  2020-06-01      103     01341433        396
    6       源采文  2020-06-02      103     00174553        497
    5       掌蓉城  2020-06-02      103     01341433        497
    4       酒博瀚  2020-06-02      101     00277553        497
    1       丰雅丽  2020-06-02      89      10271563        497
    3       齐钰    2020-06-02      101     02371493        497
    1       丰雅丽  2020-06-03      89      10271563        190
    4       酒博瀚  2020-06-03      101     00277553        190
    4       酒博瀚  2020-06-04      101     00277553        101
    2       碧千易  2020-06-13      93      02371493        277
    2       碧千易  2020-06-13      93      02371493        277
    1       丰雅丽  2020-06-13      91      02371493        277
    
  • ⑤ 计算每个用户的累加金额

    select *, sum(price) over(distribute by name sort by order_date) 
    from db_company.hotel_order;
    

    结果如下:

    hotel_order.id  hotel_order.name        hotel_order.order_date  hotel_order.price       hotel_order.code        sum_window_0
    1       丰雅丽  2020-06-01      89      10271563        89
    1       丰雅丽  2020-06-02      89      10271563        178
    1       丰雅丽  2020-06-03      89      10271563        267
    1       丰雅丽  2020-06-13      91      02371493        358
    5       掌蓉城  2020-06-01      103     01341433        103
    5       掌蓉城  2020-06-02      103     01341433        206
    6       源采文  2020-06-01      103     00174553        103
    6       源采文  2020-06-02      103     00174553        206
    2       碧千易  2020-06-13      93      02371493        186
    2       碧千易  2020-06-13      93      02371493        186
    4       酒博瀚  2020-06-02      101     00277553        101
    4       酒博瀚  2020-06-03      101     00277553        202
    4       酒博瀚  2020-06-04      101     00277553        303
    3       齐钰    2020-06-01      101     02371493        101
    3       齐钰    2020-06-02      101     02371493        202
    
  • ⑥ 查询每个用户上次的入住时间
    这里基于⑤,并且用到lag函数

    select *, 
    sum(price) over(distribute by name sort by order_date),
    lag(order_date, 1) over(distribute by name sort by order_date)
    from db_company.hotel_order;
    

    结果如下:

    hotel_order.id  hotel_order.name        hotel_order.order_date  hotel_order.price       hotel_order.code        sum_window_0    lag_window_1
    1       丰雅丽  2020-06-01      89      10271563        89      NULL
    1       丰雅丽  2020-06-02      89      10271563        178     2020-06-01
    1       丰雅丽  2020-06-03      89      10271563        267     2020-06-02
    1       丰雅丽  2020-06-13      91      02371493        358     2020-06-03
    5       掌蓉城  2020-06-01      103     01341433        103     NULL
    5       掌蓉城  2020-06-02      103     01341433        206     2020-06-01
    6       源采文  2020-06-01      103     00174553        103     NULL
    6       源采文  2020-06-02      103     00174553        206     2020-06-01
    2       碧千易  2020-06-13      93      02371493        186     NULL
    2       碧千易  2020-06-13      93      02371493        186     2020-06-13
    4       酒博瀚  2020-06-02      101     00277553        101     NULL
    4       酒博瀚  2020-06-03      101     00277553        202     2020-06-02
    4       酒博瀚  2020-06-04      101     00277553        303     2020-06-03
    3       齐钰    2020-06-01      101     02371493        101     NULL
    3       齐钰    2020-06-02      101     02371493        202     2020-06-01
    
  • ⑦ 查询每日每个人的订单金额排名

    select name, price, order_date,
    rank() over(distribute by order_date sort by price desc),
    dense_rank() over(distribute by order_date sort by price desc),
    row_number() over(distribute by order_date sort by price desc)
    from db_company.hotel_order;
    

    结果如下:

    name    price   order_date      rank_window_0   dense_rank_window_1     row_number_window_2
    源采文  103     2020-06-01      1       1       1
    掌蓉城  103     2020-06-01      1       1       2
    齐钰    101     2020-06-01      3       2       3
    丰雅丽  89      2020-06-01      4       3       4
    源采文  103     2020-06-02      1       1       1
    掌蓉城  103     2020-06-02      1       1       2
    酒博瀚  101     2020-06-02      3       2       3
    齐钰    101     2020-06-02      3       2       4
    丰雅丽  89      2020-06-02      5       3       5
    酒博瀚  101     2020-06-03      1       1       1
    丰雅丽  89      2020-06-03      2       2       2
    酒博瀚  101     2020-06-04      1       1       1
    碧千易  93      2020-06-13      1       1       1
    碧千易  93      2020-06-13      1       1       2
    丰雅丽  91      2020-06-13      3       2       3
    
  • ⑧ 查询用户在2020年,连续2天(或以上)的都有入住记录,且订单金额大于90元的流水。
    分析文件
    第一步:查询2020年订单金额大于90的订单流水,并计为t1。

    select 
      name, order_date, price, code
    from 
      db_company.hotel_order
    where 
      substring(order_date, 1, 4) = '2020' and price > 90;t1
    

    第二步:查询前一天后一天的日期数据,并计为t2。

    select
      name, order_date, price, code,
      lag(order_date, 1, '0000-00-00') over(partition by name order by order_date) lag1,
      lead(order_date, 1, '0000-00-00') over(partition by name order by order_date) lead1
    from t1;t2
    

    第三步:计算时间差,并计为t3。
    我们用:
    Ⅰ. 当前时间 - 前一天的时间(为0或者1即合法)。其中为0表示同一天有多笔订单,为1表示前一天有订单
    Ⅱ. 当前时间 - 后一天的时间(为0或则-1即合法)。其中为0表示同一天有多笔订单,为-1表示后一天有订单

    select 
      name, order_date, price, code,
      datediff(order_date, lag1) diff_lag1,
      datediff(order_date, lead1) diff_lead1
    from t2;t3
    

    第四步:同步比对时间差,得出最后流水明细

    select 
      name, order_date, price, code
    from
      t3
    where 
      diff_lag1 = 0 or diff_lag1  = 1 or diff_lead1 = -1 or diff_lead1 = 0;
    

    最后的sql为:

    select 
      name, order_date, price, code
    from
    (
      select 
        name, order_date, price, code,
        datediff(order_date, lag1) diff_lag1,
        datediff(order_date, lead1) diff_lead1
      from 
      (
        select
            name, order_date, price, code,
            lag(order_date, 1, '0000-00-00') over(partition by name order by order_date) lag1,
            lead(order_date, 1, '0000-00-00') over(partition by name order by order_date) lead1
        from 
        (
           select name, order_date, price, code
           from 
              db_company.hotel_order
           where 
              substring(order_date, 1, 4) = '2020' and price > 90
        )t1
      )t2
    )t3
    where 
      diff_lag1 = 0 or diff_lag1  = 1 or diff_lead1 = -1 or diff_lead1 = 0;
    

    最后的结果如下:

    name    order_date      price   code
    掌蓉城  2020-06-01      103     01341433
    掌蓉城  2020-06-02      103     01341433
    源采文  2020-06-01      103     00174553
    源采文  2020-06-02      103     00174553
    碧千易  2020-06-13      93      02371493
    碧千易  2020-06-13      93      02371493
    酒博瀚  2020-06-02      101     00277553
    酒博瀚  2020-06-03      101     00277553
    酒博瀚  2020-06-04      101     00277553
    齐钰    2020-06-01      101     02371493
    齐钰    2020-06-02      101     02371493
    

3. 大功告成

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