1. hive0.10及之前的版本没有row_number这个函数,假设我们现在出现如下业务场景,现在我们在hdfs上有个log日志文件,为了方便叙述,该文件只有2个字段,第一个是用户的id,第二个是当天登录的timestamp,现在我们需要求每个用户最早登录的那条记录(注意不是仅仅只要那个登录的timestamp),可以方便计算NewUser。

2. 我们的数据是这样的:

1,32
2,46
3,312
4,4643
5,54
6,456
7,437
8,5347
9,47
1,466
2,546
3,4
4,886
5,546
6,57
7,235
8,765
9,634

这里是假设的数据。

3.我们可以用hive建立一张表,其中第一个字段是id string, 第二个是login_time bigint,假设我们的表名是log。

4. 这样的场景可以用很多方法解决,但是我们可以用RowNumber函数,在0.11及以上的版本才集成到了hive中,但是我们公司用的是CDH4.5.0,hive才到0.10,所以只能自己写个这样的函数,具体的代码如下:

[java] view plaincopy

1. <span style="font-family:SimSun;font-size:14px;">import org.apache.hadoop.hive.ql.exec.UDF;  
2.   
3. public class RowN extends UDF {  
4.   
5. private static int MAX_VALUE = 50;  
6. private static String comparedColumn[] = new String[MAX_VALUE];  
7. private static int rowNum = 1;  
8.   
9. public int evaluate(Object... args) {  
10. new String[args.length];  
11. for (int i = 0; i < args.length; i++) {  
12.             columnValue[i] = args[i].toString();  
13.         }  
14. if (rowNum == 1) {  
15. for (int i = 0; i < columnValue.length; i++)  
16.                 comparedColumn[i] = columnValue[i];  
17.         }  
18.   
19. for (int i = 0; i < columnValue.length; i++) {  
20. if (!comparedColumn[i].equals(columnValue[i])) {  
21. for (int j = 0; j < columnValue.length; j++) {  
22.                     comparedColumn[j] = columnValue[j];  
23.                 }  
24. 1;  
25. return rowNum++;  
26.             }  
27.         }  
28. return rowNum++;  
29.     }  
30. }  
31. </span>

5. 稍微解释下这个UDF,首先我们的UDF函数输入是多个列的值,传入多个值表示用多个值是否相同来打序号,对于我们的场景只要1个(就是id),函数row_number(),必须带一个或者多个列参数,如ROW_NUMBER(col1, ....),它的作用是按指定的列进行分组生成行序列。在ROW_NUMBER(a,b) 时,若两条记录的a,b列相同,则行序列+1,否则重新计数。

6. 接下去关键的就是怎么取使用这个函数了,我们必须保证查出来的数据是有序的,这样才好加序号,而且要根据某个字段排序,但是如果数据量大或者我们自己设置了多个reducer咋办,这样的话我们就想到了使用distribute by和sort by的配合使用,可以使key相同的数据进入同一个reducer,这样就好办了,那么我们的hql语句其实就是一句话:

[sql] view plaincopy

1. create temporary function RowNumber as 'xxx.xxx.xxx.udf.RowNumber';  
2. select id, login_time from (select * from log distribute by id sort by id, login_time asc) tmp where RowNumber(id)=1;

结果为: 
132
 2 46
 3 4
 4 886
 5 54
 6 25
 7 75
 8 534
 9 47注:

1. 如果对distribute by不熟悉可以看另一个我的博客,有具体的解释:

2. 这个函数最关键的部分就是得先有序,所以加序号前必须保证数据有序