Hive实现:1、获取每个人最高分对应的学科和分数(学生最高分);2、获取每门课程都高于平均分的人、课程和分数(三好学生:每科高于平均分);3、获取每个学科都及格(即不低于60分)的学生

数据表和需求

表名:student, 结构和具体内容如下:
name course score
zs  Math    100
zs  Engl    80
ls  Math    90
ls  Engl    70

需求如下:
1、获取每个学生最高分对应的学科和分数
(方式一:使用row_number()开窗函数;方式二:不用开窗函数,使用join方式)
扩展:返回每一门课程和对应的最高分的学生姓名(思路:将分组的学生替换为学科即可,此处不再赘述)
2、获取每门课程都高于平均分的人、课程和分数

数据准备

set hive.exec.mode.local.auto=true;
set hive.cli.print.header=true;

create table student stored as orc as
select 'zs' as name, 'Math' as course, 100 as score union all
select 'zs' as name, 'Engl' as course, 80  as score union all
select 'ls' as name, 'Math' as course, 90  as score union all
select 'ls' as name, 'Engl' as course, 70  as score;

需求1:获取每个人最高分对应的学科和分数
第一步:使用开窗函数,用row_number(), 按name分组,每个人的分数排序,
第二步:排好序后,取出每个人的最高分即可

----------------- 方式一 使用row_number() ---------------
select 
   name,
   course,
   score
from(
    select 
        name,
        course,
        score,
        row_number() over(partition by name order by score desc) row_num
    from student
)t
where row_num = 1;
----------------- 查询结果 ---------------
name	course	score
ls	Math	90
zs	Math	100

扩展:返回每一门课程和对应的最高分的学生姓名(思路:用学科分组替换学生分组,不再赘述)

----------------- 方式二 使用join ---------------
--思路:先获取每个学生的最高分,去join原表即可拿到
select
    s.name,
    s.course,
    s.score
from student s
join(
    select 
        name,
        max(score) as max_score
    from student
    group by name
)t
on s.name = t.name and s.score = t.max_score;
----------------- 查询结果 ---------------
s.name	s.course	s.score
ls	Math	90
zs	Math	100

需求2:获取每门课程都高于平均分的人、课程和分数
减法实现:所有课程高于平均分的人 = 所有人 - 至少一门低于平均分的人

第一步:先求每门课程的平均分,得到临时表 t1
第二步:找出有某门课程小于平均分的人,得到临时表 t2
第三步:原表left join t2,让 t2 中 name 为空,则反向获取到每门课程均高于平均分的人
注意点:有一个思维转换:不容易直接找出同时多门课程都高于平均分的人,
可以反向找出只要有一门课程小于平均分,那这个人一定不是所有课程都高于平均分的人

-------- 测试查询 满足条件的人和课程;但是多行,最终每个人需转为一行数据,见下一条sql  -----
select 
   name,
   course,
   score
from abc
left join(
    select
        if(abc.score < t1.avg_score, abc.name, null) as name,
    from abc
    join (
        select
            cource
            sum(score)  / count(*) as avg_score
        from abc
        group by course
    )t1
    on abc.course = t1.course
)t2
on abc.name = t2.name
where t2.name is null;
----------------- 查询结果 ---------------
s.name	s.course	s.score
zs	Math	100
zs	Engl	80

另外注意点: if(s.score < t1.avg_score, s.name, rand()) as name
判断分数低于平均分时,如果非,则用rand()代替,这样避免后面join时出现,倾斜key

-------- 最终sql查询 满足条件的人和课程;但是多行,最终每个人转一行数据  -----

create table tmp stored as orc as
select 
   s.name,
   collect_list(concat_ws(':', array(s.course, s.score))) as detail_array,
   concat_ws(',',
             collect_list( concat_ws(':', array(s.course, s.score) ) )
             ) as detail_string 
from student s
left join(
    select
        if(s.score < t1.avg_score, s.name, rand()) as name
    from student s
    join (
        select
            course,
            sum(score) / count(*) as avg_score
        from student
        group by course
    )t1
    on s.course = t1.course
)t2
on s.name = t2.name
where t2.name is null
group by s.name;
----------------- 查询结果 ---------------
s.name	detail_array	detail_string
zs	["Math:100","Engl:80"]	Math:100,Engl:80

注意:字段类型根据最终需求看是否转 string,还是array
(1) string更容易和其他数据库间导入导出,如mysql没有array类型,可能会有一点麻烦
(2) array容易获取对应位置的数据

hive> desc tmp;
OK
col_name	data_type	comment
name                	string              	                    
detail_array        	array<string>       	                    
detail_string       	string

----------获取某一个位置的值
hive> select detail_array[1] as course_score from tmp;
course_score
Engl:80

hive> select detail_array[0] as course_score from tmp;
course_score
Math:100

3、获取每个学科都及格(>=60分)的学生

--方式一: 使用having方式,仅一个job(比较好方式) ----------
select 
name
from student_copy
group by name
having min(score)> 60;
--结果如下: ------------
ls
zs
Time taken: 1.305 seconds, Fetched: 2 row(s)

-- 比较笨重的方式:会有4个job(不建议,只是对比两个) -------
select
    distinct s.name
from student_copy s
left join (
    select
        name
    from student_copy
    where score < 60
    group by name
)t1
on s.name=t1.name
where t1.name is null;
--结果如下
ls	
zs
Time taken: 12.298 seconds, Fetched: 4 row(s)

你可能感兴趣的:(Hive,HiveSQL)