其实前一篇的Mysql 小技巧中having min()的方法是为了本篇准备的。但是当时遇到南墙,这次终于破壁找到方案。
描述: id (自增),type (aaa, bbb,ccc ,ddd),status(ok,error) 三个字段,每个type,筛选status='ok'的并且id最小的那一条记录。
Mysql:
create table having_test (id int(11), type varchar(50),status varchar(50));
mysql> select * from having_test;
+------+------+--------+
| id | type | status |
+------+------+--------+
| 1 | aaa | ok |
| 2 | aaa | error |
| 3 | aaa | ok |
| 4 | bbb | ok |
| 5 | ccc | error |
| 6 | ccc | ok |
| 7 | ddd | error |
+------+------+--------+
mysql> select * from having_test where status='ok' group by type having min(id);
+------+------+--------+
| id | type | status |
+------+------+--------+
| 1 | aaa | ok |
| 4 | bbb | ok |
| 6 | ccc | ok |
+------+------+--------+
mysql中很简单就实现了,先 group 然后having ,但是hive上不是完全支持sql语法的,在hive上会不会这么简单呢,答案是否定的。
HIVE 中:
create table tmp_wjk_having_test (id int, type string, status string) ROW FORMAT DELIMITED FIELDS TERMINATED BY '\t' ;
load data local inpath '/tmp/load.csv' overwrite into table tmp_wjk_having_test;
select * from tmp_wjk_having_test;
1 aaa ok
2 aaa error
3 aaa ok
4 bbb ok
5 ccc error
6 ccc ok
7 ddd error
select * from tmp_wjk_having_test where status='ok' group by type having min(id);
FAILED: Error in semantic analysis: Line 1:73 Expression not in GROUP BY key 'id'
# hive 不支持这种写法。还是要用子查询
select * from tmp_wjk_having_test t1 join (
select min(id) id from tmp_wjk_having_test where status='ok' group by type) t2
on t1.id=t2.id ;
1 aaa ok 1
4 bbb ok 4
6 ccc ok 6
子查询对于小数据集没有影响,但是应用到大数据上最好的是只过一边表,然后就拿出结果。所以还在想新的方案。
select *,min(id) ii from tmp_wjk_having_test where status='ok' group by type ;
aaa 1 1
bbb 4 4
ccc 6 6
这种方案可行。问题点:
1. 为什么min(id)的条件明明是写到了select 中非where ,但是确起到了筛选的作用?
2. 为什么明明是select * ,min(id) 但是最后是拿到了3列(type , id , min(id) ), 如果写成 select type ,min(id) 就只拿到2列( type ,min(id) ) .
select type,min(id) ii from tmp_wjk_having_test where status='ok' group by type;
aaa 1
bbb 4
ccc 6
++++更新 2014.11.11
3、一般可行方案:
2列 : select type,min(id) ii from tmp_wjk_having_test where status='ok' group by type;
多列:select t1.* from having_test t1 join (select name,min(age) mm from having_test group by name ) t2 on t1.name = t2.name and t1.age=t2.mm ;