hive 窗口函数Over

来一个需求:求每个部门的员工信息以及部门的平均工资。在mysql中如何实现呢。

-- 第一种写法
SELECT emp.*, avg_sal
FROM emp
	JOIN (
		SELECT deptno
			, round(AVG(ifnull(sal, 0))) AS avg_sal
		FROM emp
		GROUP BY deptno
	) t
	ON emp.deptno = t.deptno
ORDER BY deptno;

-- 第二种写法
select A.*,(select avg(ifnull(sal,0)) from emp B where B.deptno = A.deptno ) from emp A;

以后看见这种既要明细信息,也要聚合信息的题目,直接开窗!

上案例:

数据order.txt

姓名,购买日期,购买数量
saml,2018-01-01,10
saml,2018-01-08,55
tony,2018-01-07,50
saml,2018-01-05,46
tony,2018-01-04,29
tony,2018-01-02,15
saml,2018-02-03,23
mart,2018-04-13,94
saml,2018-04-06,42
mart,2018-04-11,75
mart,2018-04-09,68
mart,2018-04-08,62
neil,2018-05-10,12
neil,2018-06-12,80

-1. 创建order表:
create table if not exists t_order
(
    name      string,
    orderdate string,
    cost      int
)  row format delimited fields terminated by ',';
-2. 加载数据:
load data local inpath "/home/hivedata/order.txt" into table t_order;

指标一:需求:查询每个订单的信息,以及订单的总数

不使用开窗函数的写法
select *,(select count(1) from t_order) as `订单总数` from t_order ;

使用开窗函数的写法:
select *, count(*) over() from t_order;

开窗函数一般不单独使用,而是跟另外一些函数一起使用,比如 count, over() 这个的窗口是多大呢?over() 是整个数据集。

窗口其实就是范围,比如统计男女比例?必须知道窗口,是统计整个班级还是统计整个学校,班级和学校就是窗口。

窗口函数是针对每一行数据的.
如果over中没有指定参数,默认窗口大小为全部结果集

指标二:查询在2018年1月份购买过的顾客购买明细及总次数。

select *,count(*) over()
from t_order
where substr(orderdate,1,7) = '2018-01';

指标三:查询在2018年1月份购买过的顾客购买明细及总人数。

select *,count(distinct name) over()
from t_order
where substr(orderdate,1,7) = '2018-01';

还有没有其他的写法:
错误的写法:group by 语句,select 后面只能跟分组字段和聚合函数
select *,count(distinct name) over()
from t_order
where substr(orderdate,1,7) = '2018-01' group by name;
正确写法:
-- 假如使用group by
select *,(select count(1) from (
    select name from t_order where substr(orderdate,1,7) ='2018-01' group by name
  ) t) from t_order  where substr(orderdate,1,7) ='2018-01';
结论是:太麻烦了

distribute by子句:

在over窗口中进行分组,对某一字段进行分组统计,窗口大小就是同一个组的所有记录

语法:
over(distribute by colname[,colname.....])

指标四:查看顾客的购买明细及月购买总额

错误写法:明细信息是不能跟聚合函数一起使用的,聚合多个信息变一个。
select *,sum(cost) from t_order ;
可以这么写:
select *,(select sum(cost) from t_order) from t_order ;

如果非要按照第一个写法,需要开窗
select *,sum(cost) over() from t_order ;
底层原理:先查询到第一条数据,saml,2018-01-01,10,然后进行 sum统计,统计的窗口是整个数据集。
接着查询第二条数据 xxxxx,然后进行 sum统计,统计的窗口是整个数据集,依次类推。

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

接着继续编写咱们的需求:
select *,sum(cost) over(distribute by substr(orderdate,1,7) ) from t_order ;

t_order.name    t_order.orderdate       t_order.cost    sum_window_0
saml    2018-01-01      10      205
saml    2018-01-08      55      205
tony    2018-01-07      50      205
saml    2018-01-05      46      205
tony    2018-01-04      29      205
tony    2018-01-02      15      205
saml    2018-02-03      23      23
mart    2018-04-13      94      341
saml    2018-04-06      42      341
mart    2018-04-11      75      341
mart    2018-04-09      68      341
mart    2018-04-08      62      341
neil    2018-05-10      12      12
neil    2018-06-12      80      80
Time taken: 2.128 seconds, Fetched: 14 row(s)

指标5:需求:查看顾客的购买明细及每个顾客的月购买总额

select *,sum(cost) over(distribute by name,month(orderdate) ) from t_order ;

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

窗口期:这个词我们经常听到,比如:穷人的孩子,上升渠道的窗口期马上就过去了。

sort by子句

sort by子句会让输入的数据强制排序 (强调:当使用排序时,窗口会在组内逐行变大

语法:

语法:  over([distribute by colname] [sort by colname [desc|asc]])

需求6:查看顾客的购买明细及每个顾客的月购买总额,并且按照日期降序排序

select *,sum(cost) over(distribute by name,month(orderdate) sort by orderdate desc ) from t_order ;

注意:可以使用partition by + order by 组合来代替distribute by+sort by组合

select *,sum(cost) over(partition by name,month(orderdate) order by orderdate desc ) from t_order ;

注意:也可以在窗口函数中,只写排序,窗口大小是全表记录

select *,sum(cost) over(order by orderdate desc ) from t_order ;

window 子句

如果要对窗口的结果做更细粒度的划分,那么就使用window子句,常见的有下面几个
PRECEDING:往前 
FOLLOWING:往后 
CURRENT ROW:当前行 
UNBOUNDED:起点,
UNBOUNDED PRECEDING:表示从前面的起点, 
UNBOUNDED FOLLOWING:表示到后面的终点 

解析这句话:
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 t_order;

运行结果:

name    orderdate       cost    sample1 sample2 sample3 sample4 sample5 sample6 sample7
mart    2018-04-08      62      661     299     62      62      62      130     299
mart    2018-04-09      68      661     299     130     130     130     205     237
mart    2018-04-11      75      661     299     205     205     143     237     169
mart    2018-04-13      94      661     299     299     299     169     169     94
.....

需求7:查看顾客到目前为止的购买总额

select *,sum(cost) over(rows between UNBOUNDED PRECEDING and current row)  
   from t_order;

需求8:求每个顾客最近三次的消费总额

select *,sum(cost) over(partition by name order by orderdate   rows between 2 PRECEDING and current row)  from t_order;

t_order.name    t_order.orderdate       t_order.cost    sum_window_0
mart    2018-04-08      62      62
mart    2018-04-09      68      130
mart    2018-04-11      75      205
mart    2018-04-13      94      237
neil    2018-05-10      12      12
neil    2018-06-12      80      92
saml    2018-01-01      10      10
saml    2018-01-05      46      56
saml    2018-01-08      55      111
saml    2018-02-03      23      124
saml    2018-04-06      42      120
tony    2018-01-02      15      15
tony    2018-01-04      29      44
tony    2018-01-07      50      94

 回过头来看以前的需求:求每个部门的员工信息以及部门的平均工资

select *,avg(sal) over(partition by deptno) from emp;

注意:默认mysql老版本没有支持,在最新的8.0版本中支持, Oracle和Hive中都支持窗口函数。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值