普通的聚合函数聚合的行集是组,开窗函数聚合的行集是窗口。
因此,普通的聚合函数每组(Group by)只返回一个值,而开窗函数则可为窗口中的每行都返回一个值。
简单理解,就是对查询的结果多出一列,这一列可以是聚合值,也可以是排序值。 开窗函数一般分为两类,聚合开窗函数和排序开窗函数。

OVER():指定分析函数工作的数据窗口大小,这个数据窗口大小可能会随着行的变而变化
CURRENT ROW:当前行
n PRECEDING:往前n行数据
n FOLLOWING:往后n行数据
UNBOUNDED:起点,UNBOUNDED PRECEDING 表示从前面的起点, UNBOUNDED FOLLOWING表示到后面的终点
LAG(col,n):往前第n行数据
LEAD(col,n):往后第n行数据
NTILE(n):把有序分区中的行分发到指定数据的组中,各个组有编号,编号从1开始,对于每一行,NTILE返回此行所属的组的编号。注意:n必须为int类型。

测试数据:

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

需求:
(1)查询在2017年4月份购买过的顾客及总人数
(2)查询顾客的购买明细及月购买总额
(3)上述的场景,要将cost按照日期进行累加
(4)查询顾客上次的购买时间
(5)查询前20%时间的订单信息

建表、导数:

create table business(name string, orderdate string, cost int)
row format delimited fields terminated by ',';
load data local inpath "/opt/data/business.txt" into table business;

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

select
name, count(*) as cnt
from business where date_format(orderdate,'yyyy-MM')='2017-04'
group by name;

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

select
name,
orderdate,
cost,
sum(cost) over (partition by month(orderdate)) as month_sum
from business order by name,orderdate;

(3)上述的场景,要将cost按照日期进行累加

select
name,orderdate,cost,
sum(cost) over (partition by name order by orderdate rows between unbounded preceding and current row) as accumulate_cost,
--按name分组,按orderdate排序,求从组内首行到当前行之和
sum(cost) over(partition by name order by orderdate) as accumulate_cost2,
--效果和上个一样
sum(cost) over () as total_cost,
--所有cost求和
sum(cost) over (partition by name) as total_cost_by_person,
--每个name组内之和
sum(cost) over (partition by name order by orderdate rows between 1 preceding and current row) as double_cost,
--组内按orderdate排序,前一行和当前行之和
sum(cost) over (partition by name order by orderdate rows between 1 preceding and
1 following) as triple_cost,
--组内按orderdate排序,前一行、当前行、下一行,三行之和
sum(cost) over (partition by name order by orderdate rows between current row and
unbounded following) as total_next_cost
--组内按orderdate排序,当前行至组内尾行之和
from business;

(4)查询顾客上次的购买时间

select
name,
orderdate,
lag(orderdate,1) over(partition by name order by orderdate) as time2
from business;

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

ntile(n)用于将分组数据按照顺序切分成n片,返回当前记录所在的切片值,经常用来取前30% 带有百分之多少比例的记录什么的。

select name,orderdate,cost,
ntile(5) over (order by orderdate) sorted
from business;

查询20%即sorted=1

select t.* from (
select
  name,
  orderdate,
  cost,
  ntile(5) over (order by orderdate) sorted
from business
where t.sorted=1;

扩展:
求前40%时间和后60%时间内的平均cost

--<1>按照orderdate升序拆分成5分

select
name,
orderdate,
cost,
ntile(5) over (order by orderdate) as prt
from business;

--<2>前2份prt标识为1,是前40%的时间;后3份prt标识为2,是后60%的时间

select 
t.name,
t.orderdate,
t.cost,
case when prt in (1,2) then 1 else 2 end as new_prt
from(
    select 
    name,
    orderdate,
    cost,
    ntile(5) over (order by orderdate) as prt
    from business

--<3>--取每个new_prt的平均值

select 
new_prt,
max(case when new_prt=1 then "first_40%_avg_cost" when new_prt=2 then "last_60%_avg_cost" end) as prt_cost_name,
cast(avg(cost)as decimal(10,2)) as avg_cost
from(
    select 
    t.name,
    t.orderdate,
    t.cost,
    case when prt in (1,2) then 1 else 2 end as new_prt
    from(
        select 
        name,
        orderdate,
        cost,
        ntile(5) over (order by orderdate) as prt
        from business
group by q.new_prt;