目 录
0 需求分析
1 实现
2 小 结
现有交易数据表user_trade如下:
user_id 用户id
pay_amount 用户支付额度
现在老板想知道支付金额在前20%的用户。
输出要求如下:
user_id用户名(前20%的用户)(1)数据准备
1001,100.7 1001,70.8 1002,50.4 1003,70 1005,100.5 1002,80.1 1003,36.7 1004,38.5 1003,50 1004,30 1004,300.5 1005,500 1005,3 1006,260.1 1007,360.4 1008,680(2)创建表
drop table if exists dan_test.user_trade CREATE TABLE dan_test.user_trade ( user_id string, pay_amount double ) ROW format delimited FIELDS TERMINATED BY ",";(3) 加载数据
load data local inpath "/home/centos/dan_test/user_trade.txt" into table user_trade;(4)需求实现
分析:
Ntile函数使用
可以看成是:它把有序的数据集合平均分配到指定的数量(num)个桶中, 将桶号分配给每一行。如果不能平均分配,则优先分配较小编号的桶,并且各个桶中能放的行数最多相差1。(这个算法在很多当中使用,spark中数据分片的时候也是这个算法,只不过是不均匀的时候,优先分配给较大编号的分片,如下图所示) 语法是:ntile (num) over ([partition_clause] order_by_clause) as your_bucket_num然后可以根据桶号,选取前或后 n分之几的数据。数据会完整展示出来,只是给相应的数据打标签;具体要取几分之几的数据,需要再嵌套一层根据标签取出。NTILE不支持ROWS BETWEEN,比如 NTILE(2) OVER(PARTITION BY cookieid ORDER BY createtime ROWS BETWEEN 3 PRECEDING AND CURRENT ROW)本题要求的是支付金额前20%的用户,通过上述ntile函数分析 可知需要分5个桶,是对数据用户支付的总金额进行标签,然后求出对应的用户。具体SQL如下:
select a.user_id ,a.sum_pay_amount ,a.level from( select user_id ,sum(pay_amount) as sum_pay_amount ,ntile(5) over(order by sum(pay_amount) desc) as level from user_trade group by user_id ) a where a.level = 1 -------------------------------------------------------------------------------- OK a.user_id a.sum_pay_amount a.level 1008 680.0 1 1005 603.5 1本题错误写法:
select user_id ,sum(pay_amount) as sum_pay_amount ,ntile(5) over(partition by user_id order by sum(pay_amount) desc) as level from user_trade FAILED: SemanticException Failed to breakup Windowing invocations into Groups. At least 1 group must only depend on input columns. Also check for circular dependencies. Underlying error: org.apache.hadoop.hive.ql.parse.SemanticException: Line 3:34 Expression not in GROUP BY key 'user_id'根据错误提示写测试案例:
select user_id ,sum(pay_amount) as sum_pay_amount from user_trade FAILED: SemanticException [Error 10025]: Line 1:7 Expression not in GROUP BY key 'user_id'依然报错,但如下的SQL是正确的
select sum(pay_amount) as sum_pay_amount from user_trade对比说明:说明hive中是可以单独使用sum()等聚合函数,而不使用group by,但是不使用group by使用聚合函数,只能出现聚合函数的字段,而不能有其他字段的出现。
这个案例也说明窗口函数的使用也是基于group by之后的结果进行开窗,在group by 之后的结果再进行计算,可以理解为group by的结果作为子表供窗口函数使用。
本文主要通过案例来分析ntile()函数的使用方法,ntile()函数主要用于对数据进行分片、分桶,其算法为如果数据能均匀分配的话就按照平均分配,不能均匀分配的话,优先将数据分配到编号较小的桶中。ntile()函数主要用来求某个数据的前或后百分比问题题,在统计中具有重要意义。
