当前位置: 代码迷 >> 综合 >> Hive-窗口函数,详细分析,案例实践
  详细解决方案

Hive-窗口函数,详细分析,案例实践

热度:93   发布时间:2024-02-06 14:22:03.0

Hive-窗口函数,详细分析,案例实践

文章目录

    • Hive-窗口函数,详细分析,案例实践
      • 窗口函数介绍
      • 窗口函数分析
      • 案例实践一
      • 案例实践二

窗口函数介绍

LEAD(col,n, default_val):表示往后第n行数据,col是字段,n表示第几行,default_val 如果没有往后第n行则用默认值代替

LAG(col,n,default_val):表示往前第n行数据,col是字段,n表示第几行,default_val 如果没有往前第n行则用默认值代替

FIRST_VALUE (col,true/false):表示当前窗口下的第一个值,第二个参数为true,跳过空值

LAST_VALUE (col,true/false):表示当前窗口下的最后一个值,第二个参数为true,跳过空值

标准的聚合函数
COUN()TSUM()MIN()MAX()AVG()

排名和分析函数
RANK()ROW_NUMBER()DENSE_RANK()NTILE()

窗口函数分析

窗口函数=函数+窗口( 窗口:可以限定函数计算的范围)

语法: 窗口函数() over([partition by 字段...] [order by 字段..] [窗口子句])

窗口函数本身的执行顺序:

  1. over()最大的窗口范围
  2. partition by 表示对 over 划分窗口再次划分
  3. order by 对 over/partition 的窗口数据按照字段做排序 ascdesc
  4. 窗口子句 对于over/partition 内的数据,给定函数的运算范围
  5. 窗口函数() 对每一行数据做 窗口范围内的运算

over():是表示最大的窗口范围

partition by 字段:按照字段是否相同来划分更细的窗口,将字段相同的数据分到同一个细窗口里面

窗口子句:

  • 当有只有 over 的时候,窗口子句的范围是 over 指定的范围
  • 当有 partition 的时候,窗口子句的范围是 partition by 过后所有细窗口范围

并不是所有行数都需要窗口子句,以下窗口函数没有窗口字句
Rank(),NTile(),DenseRank(),ROW_NUMBER(),Lead(),Lag ()

有order by 但没有窗口子句的时候,默认范围 (上无边界 到 当前行)

  • the WINDOW specification defaults to rows BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW.

order by 和 窗口子句都没有的时候,默认范围 (上无边界 到 下无边界)

  • the WINDOW specification defaults to ROW BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING.

窗口子句格式:

(ROWS | RANGE) BETWEEN (UNBOUNDED | [num]) PRECEDING AND ([num] PRECEDING | CURRENT ROW | (UNBOUNDED | [num]) FOLLOWING)(ROWS | RANGE) BETWEEN CURRENT ROW AND (CURRENT ROW | (UNBOUNDED | [num]) FOLLOWING)(ROWS | RANGE) BETWEEN [num] FOLLOWING AND (UNBOUNDED | [num]) FOLLOWING

案例实践一

1)数据准备:name,orderdate,cost

jack,2020-01-01,10
tony,2020-01-02,15
jack,2020-02-03,23
tony,2020-01-04,29
jack,2020-01-05,46
jack,2020-04-06,42
tony,2020-01-07,50
jack,2020-01-08,55
mart,2020-04-08,62
mart,2020-04-09,68
neil,2020-05-10,12
mart,2020-04-11,75
neil,2020-06-12,80
mart,2020-04-13,94

2)需求

(1)查询在2020年4月份购买过的顾客及总人数

(2)查询顾客的购买明细及月购买总额

(3)上述的场景, 将每个顾客的cost按照日期进行累加

(4)查询顾客购买明细以及上次的购买时间和下次购买时间

(5)查询顾客每个月第一次的购买时间 和 每个月的最后一次购买时间

(6)查询前20%时间的订单信息

3)创建本地business.txt,导入数据

[qinjl@hadoop102 datas]$ vi business.txt

4)创建hive表并导入数据

create table business(
name string, 
orderdate string,
cost int
) ROW FORMAT DELIMITED FIELDS TERMINATED BY ',';load data local inpath "/opt/module/hive/datas/business.txt" into table business;

5)按需求查询数据

(1)查询在2020年4月份购买过的顾客及总人数

select name,count(*) over() 
from business 
where substring(orderdate,1,7) = '2020-04' 
-- where month(orderdate)=4 
-- where date_format(orderdate,'yyyy-MM')='2020-04' 
group by name;
+-------+-----------------+
| name  | count_window_0  |
+-------+-----------------+
| mart  | 2               |
| jack  | 2               |
+-------+-----------------+

(2)查询顾客的购买明细及月购买总额

select 
name, 
orderdate, 
cost, 
sum(cost) over(partition by name,month(orderdate)) name_month_cost
from business;
+-------+-------------+-------+------------------+
| name  |  orderdate  | cost  | name_month_cost  |
+-------+-------------+-------+------------------+
| jack  | 2020-01-05  | 46    | 111              |
| jack  | 2020-01-08  | 55    | 111              |
| jack  | 2020-01-01  | 10    | 111              |
| jack  | 2020-02-03  | 23    | 23               |
| jack  | 2020-04-06  | 42    | 42               |
| mart  | 2020-04-13  | 94    | 299              |
| mart  | 2020-04-11  | 75    | 299              |
| mart  | 2020-04-09  | 68    | 299              |
| mart  | 2020-04-08  | 62    | 299              |
| neil  | 2020-05-10  | 12    | 12               |
| neil  | 2020-06-12  | 80    | 80               |
| tony  | 2020-01-04  | 29    | 94               |
| tony  | 2020-01-02  | 15    | 94               |
| tony  | 2020-01-07  | 50    | 94               |
+-------+-------------+-------+------------------+

(3)将每个顾客的cost按照日期进行累加

select name,orderdate,cost, 
sum(cost) over() as sample1,--所有行相加 
sum(cost) over(partition by name) as sample2,--按name分组,组内数据相加 
sum(cost) over(partition by name order by orderdate) as sample3,--按name分组,组内数据累加 
sum(cost) over(partition by name order by orderdate rows between UNBOUNDED PRECEDING and current row ) as sample4 ,--和sample3一样,由起点到当前行的聚合 
sum(cost) over(partition by name order by orderdate rows between 1 PRECEDING and current row) as sample5, --当前行和前面一行做聚合 
sum(cost) over(partition by name order by orderdate rows between 1 PRECEDING AND 1 FOLLOWING ) as sample6,--当前行和前边一行及后面一行 
sum(cost) over(partition by name order by orderdate rows between current row and UNBOUNDED FOLLOWING ) as sample7 --当前行及后面所有行 
from business;
  • rows必须跟在Order by 子句之后,对排序的结果进行限制,使用固定的行数来限制分区中的数据行数量

(4)查询顾客购买明细以及上次的购买时间和下次购买时间

select 
name,orderdate,cost, 
lag(orderdate,1,'1970-01-01') over(PARTITION by name order by orderdate) prev_time, 
lead(orderdate,1,'1970-01-01') over(PARTITION by name order by orderdate) next_time
from business;
+-------+-------------+-------+-------------+-------------+
| name  |  orderdate  | cost  |  prev_time  |  next_time  |
+-------+-------------+-------+-------------+-------------+
| jack  | 2020-01-01  | 10    | 1970-01-01  | 2020-01-05  |
| jack  | 2020-01-05  | 46    | 2020-01-01  | 2020-01-08  |
| jack  | 2020-01-08  | 55    | 2020-01-05  | 2020-02-03  |
| jack  | 2020-02-03  | 23    | 2020-01-08  | 2020-04-06  |
| jack  | 2020-04-06  | 42    | 2020-02-03  | 1970-01-01  |
| mart  | 2020-04-08  | 62    | 1970-01-01  | 2020-04-09  |
| mart  | 2020-04-09  | 68    | 2020-04-08  | 2020-04-11  |
| mart  | 2020-04-11  | 75    | 2020-04-09  | 2020-04-13  |
| mart  | 2020-04-13  | 94    | 2020-04-11  | 1970-01-01  |
| neil  | 2020-05-10  | 12    | 1970-01-01  | 2020-06-12  |
| neil  | 2020-06-12  | 80    | 2020-05-10  | 1970-01-01  |
| tony  | 2020-01-02  | 15    | 1970-01-01  | 2020-01-04  |
| tony  | 2020-01-04  | 29    | 2020-01-02  | 2020-01-07  |
| tony  | 2020-01-07  | 50    | 2020-01-04  | 1970-01-01  |
+-------+-------------+-------+-------------+-------------+

(5)查询顾客每个月第一次的购买时间 和 每个月的最后一次购买时间

select
name, orderdate, cost, 
FIRST_VALUE(orderdate) over(partition by name,month(orderdate) order by orderdate 
rows between UNBOUNDED PRECEDING and UNBOUNDED FOLLOWING) first_time,
LAST_VALUE(orderdate) over(partition by name,month(orderdate) order by orderdate 
rows between UNBOUNDED PRECEDING and UNBOUNDED FOLLOWING) last_time
from business;
+-------+-------------+-------+-------------+-------------+
| name  |  orderdate  | cost  | first_time  |  last_time  |
+-------+-------------+-------+-------------+-------------+
| jack  | 2020-01-01  | 10    | 2020-01-01  | 2020-01-08  |
| jack  | 2020-01-05  | 46    | 2020-01-01  | 2020-01-08  |
| jack  | 2020-01-08  | 55    | 2020-01-01  | 2020-01-08  |
| jack  | 2020-02-03  | 23    | 2020-02-03  | 2020-02-03  |
| jack  | 2020-04-06  | 42    | 2020-04-06  | 2020-04-06  |
| mart  | 2020-04-08  | 62    | 2020-04-08  | 2020-04-13  |
| mart  | 2020-04-09  | 68    | 2020-04-08  | 2020-04-13  |
| mart  | 2020-04-11  | 75    | 2020-04-08  | 2020-04-13  |
| mart  | 2020-04-13  | 94    | 2020-04-08  | 2020-04-13  |
| neil  | 2020-05-10  | 12    | 2020-05-10  | 2020-05-10  |
| neil  | 2020-06-12  | 80    | 2020-06-12  | 2020-06-12  |
| tony  | 2020-01-02  | 15    | 2020-01-02  | 2020-01-07  |
| tony  | 2020-01-04  | 29    | 2020-01-02  | 2020-01-07  |
| tony  | 2020-01-07  | 50    | 2020-01-02  | 2020-01-07  |
+-------+-------------+-------+-------------+-------------+

(6)查询前20%时间的订单信息

select * from (select name,orderdate,cost, ntile(5) over(order by orderdate) sortedfrom business
) t
where t.sorted = 1;
+---------+--------------+---------+-----------+
| t.name  | t.orderdate  | t.cost  | t.sorted  |
+---------+--------------+---------+-----------+
| jack    | 2020-01-01   | 10      | 1         |
| tony    | 2020-01-02   | 15      | 1         |
| tony    | 2020-01-04   | 29      | 1         |
+---------+--------------+---------+-----------+

案例实践二

RANK():排序相同时会重复,总数不会变

DENSE_RANK():排序相同时会重复,总数会减少

ROW_NUMBER():会根据顺序计算

1)数据准备

name	subject	score
燕青	语文	87
燕青	数学	95
燕青	英语	68
宋江	语文	94
宋江	数学	56
宋江	英语	84
林冲	语文	64
林冲	数学	86
林冲	英语	84
朱仝	语文	65
朱仝	数学	85
朱仝	英语	78

2)需求

计算每门学科成绩排名。

3)创建本地score.txt,导入数据

[qinjl@hadoop102 datas]$ vi score.txt

4)创建hive表并导入数据

create table score(
name string,
subject string, 
score int) 
row format delimited fields terminated by "\t";
load data local inpath '/opt/module/hive/datas/score.txt' into table score;

5)按需求查询数据

select name,
subject,
score,
rank() over(partition by subject order by score desc) rp,
dense_rank() over(partition by subject order by score desc) drp,
row_number() over(partition by subject order by score desc) rmp
from score;
+-------+----------+--------+-----+------+------+
| name  | subject  | score  | rp  | drp  | rmp  |
+-------+----------+--------+-----+------+------+
| 燕青   | 数学     | 95     | 1   | 1    | 1    |
| 林冲   | 数学     | 86     | 2   | 2    | 2    |
| 朱仝   | 数学     | 85     | 3   | 3    | 3    |
| 宋江   | 数学     | 56     | 4   | 4    | 4    |
| 林冲   | 英语     | 84     | 1   | 1    | 1    |
| 宋江   | 英语     | 84     | 1   | 1    | 2    |
| 朱仝   | 英语     | 78     | 3   | 2    | 3    |
| 燕青   | 英语     | 68     | 4   | 3    | 4    |
| 宋江   | 语文     | 94     | 1   | 1    | 1    |
| 燕青   | 语文     | 87     | 2   | 2    | 2    |
| 朱仝   | 语文     | 65     | 3   | 3    | 3    |
| 林冲   | 语文     | 64     | 4   | 4    | 4    |
+-------+----------+--------+-----+------+------+
  相关解决方案