hive常考SQL题

sql执行顺序:from ->  on -> join -> where ->  group by  -> 聚集函数 -> having -> having ->  select  ->distinct -> union -> order by -> limit

(1)分组求最大值

  1、单表分组最大

//在每一個州中找出最大面積的國家,列出洲份 continent, 國家名字 name 及面積 area
SELECT continent, name, area 
FROM world x
WHERE area >= ALL(    //子查询相当于查找每一个州的所有area =>笛卡尔乘积,X里的每一个area都会与y的area比较(前提是continent相等)
    SELECT area FROM world y 
  WHERE y.continent=x.continent
  AND area>0
)

//有些國家的人口是同洲份的所有其他國的3倍或以上。列出 國家名字name 和 洲份 continent。
SELECT name,continent FROM world x
WHERE x.population / 3 >= ALL(//子查询相当于找到每州中所有符合的population
    SELECT population FROM world y 
  WHERE y.continent = x.continent AND population >0 AND y.name != x.name
)

  2、两表分组求最大

//每个部门工资最高的员工
方法1
SELECT Department.name AS 'Department',Employee.name AS 'Employee',Salary
FROM Employee JOIN Department ON Employee.DepartmentId = Department.Id
WHERE (Employee.DepartmentId , Salary) IN (
    SELECT 
    DepartmentId, MAX(Salary)
    FROM Employee GROUP BY DepartmentId
);

方法2
select Department.Name Department,e1.Name Employee,e1.Salary Salary
from Employee e1 join Department on DepartmentId=Department.Id
where Salary >=ALL(
    select Salary
    from Employee e2
    where e1.DepartmentId=e2.DepartmentId
);

  3、分组求topN

SQL写法
SELECT d.Name AS 'Department', e1.Name AS 'Employee', e1.Salary
FROM Employee e1 JOIN Department d ON e1.DepartmentId = d.Id
WHERE 3 > (
    SELECT COUNT(DISTINCT e2.Salary)
    FROM Employee e2
    WHERE e1.Salary < e2.Salary AND e1.DepartmentId = e2.DepartmentId
);

取分组前3的代码
SELECT e1.Salary
FROM Employee AS e1
WHERE 3 >(
    SELECT count(DISTINCT e2.Salary)
    FROM Employee AS e2
    WHERE e1.Salary <e2.Salary AND e2.DepartmentId = e1.DepartmentId
);

HQL写法
select name,score
from(
    select name,score,row_number() over(partition by name order by score desc) rank
    from stu_score
)tmp_table
where tmp_table.rank<=3;

(2)利用row_number()和over()分组求topN

select name,score,rank
from (
    select name,score,row_number() over(partition by name order by score desc) rank
    from stu_score
)t
where rank<=3

解析:利用排序函数row_number()和开窗函数over(),其中排序函数row_number()是顺序排序,dense_rank()是连续排序,rank()函数是跳跃排序。

(3)利用row_number()和over()求用户连续登录最长天数

select uid,max(continuous_days)
from(
    select uid,date_sub(date1-rank),count(1) as continuous_days
    from(
        select uid,date1,row_number() over(partition by uid order by date1) rank
        from user_login
    )
    group by uid,date_sub(date1-rank)
)
group by uid;

解析:其中date_sub(2019-12-31,1)=2019-12-30

(4)求留存率

  1、先求每天的活跃用户数,记为A表

select dayNo,count(distinct(uid)) num1
from user_login
group by dayNo

  2、每天留存数,记为B表

select u.dayNo,count(distinct(s.uid)) num2
from userInfo u1 left join userInfo u2 on u1.uid=u2.uid;
where datediff(u2.dayNo,u1.dayNo)=1
group by u.dayNo

  3、留层率

select t1.date,t2.num2/t1.num2
from (
    select date,count(distinct(user_id)) num1
    from login 
    group by date
) t1 join (
    select u1.date,count(distinct(u2.user_id)) num2
    from login u1 left join login u2 on u1.user_id=u2.user_id;
    where datediff(u2.date,u1.date)=1
    group by u1.date
) t2 on t1.date=t2.date
union
select date,0.000 as p
from login
where date not in(
  select t2.date
  from t2
)

解析:利用A表和B表即可求出哪天的留存率。datediff(2019-12-30,2019-12-31)=1,若求3天留存,则将datediff(2019-12-30,2019-12-31)=3

(5)case when与group by

  1、利用case when构造多个字段

FName    FCourse    FScore              FName    语文    数学    英语
张三         语文          95.0           张三     95      0       0
张三         数学          80.0           张三     0       80.0    0
张三         英语          70.0    =>     张三     0       0       70
李四         语文          88.0           李四     88.0    0       0           
李四         数学          90.0           李四     0       87.0    0
李四         英语          70.0           李四     0       0       86.0

答案:
FName    语文    数学    英语
张三         95      80      70  
李四         88      87      86
select 
 FName ,
 max(case FCourse when '语文' then FScore else 0 end) as '语文',
 max(case FCourse when '数学' then FScore else 0 end) as '数学',
 max(case FCourse when '英语' then FScore else 0 end) as '英语'
from score
group by FName

  2、利用case when构造单个字段

解法一:
select FName,
 case 
 when FCourse='数学' then '理科'  else '文科' end as '科别', sum(FScore) as '总分'
from score
group by FName,case when FCourse='数学' then '理科' else '文科' end 
order by FName

解法二
select FName, FType as '科别', sum(FScore) as '总分' 
from (
 select FName, FCourse, FScore, FType = case when FCourse in ('语文','英语') then '文科' else '理科' end 
 from score
) as a
group by FName,FType
order by FName

(6)登录表:t_login:  user_id,login_time,login_ip,支付表:t_pay:  user_id,pay_time,pay_money

  1、用1个SQL统计出 登录总次数、登录总人数、登录但未支付的总人数

select count(1),count(distinct a.user_id),
count(distinct case when b.user_id is null then a.user_id end)
from t_login as a left join t_pay as b on a.user_id = b.user_id
或者
select,count1,count2,count(distinct(user_id))
from t_login t3 join (
    select t1.user_id,t2.pay_time,count(login_ip) count1,
    count(distinct(t1.user_id)) count2
    from t_login t1 left join t_pay t2 on t1.user_id=t2.user_id
)t on t.user_id=t3.user_id
where pay_time=null;

  2、查询出每个用户最近的一条登录数据 user_id,login_time,login_ip

select user_id,login_time,login_ip
(select user_id,login_time,login_ip,
row_number over(partition by user_id order by login_time desc) new_time
from t_login) t
where new_time=1;

  3、按总支付金额范围分组统计 支付的总人数、支付的平均金额

select
case
when pay_money >= 1 and pay_money <= 100 then '1~100'
when pay_money >= 101 and pay_money <= 1000 then '101~1000'
when pay_money >= 1001 then '1001+' end pay_limits,
count(distinct(user_id)),
avg(pay_money)
from
t_pay
group by
    case
    when pay_money >= 1 and pay_money <= 100 then '1~100'
    when pay_money >= 101 and pay_money <= 1000 then '101~1000'
    when pay_money >= 1001 then '1001+' end

(7)多表join

  

select t1.name,t2.name,t3.name
from (city t1 join city t2 on t1.id=t2.parentID) join city t3 on t2.id=t3.parentID

(8)利用explode行转列

      ===》

  

解析:explode(col_name):将hive中的一列的array或者map分成多行,如果是字符串类型则利用split将其变成数组;表movie_info与虚表temp_table利用lateral view进行相关联,其中temp_table表中的字段为category_type

(9)列转行

  

  

   将列表中一个id可能会占用多行转换为每个user占一行的目标表格式

   

 解析:concat(string1,string2,... ):连接括号内字符串,数量不限;

    concat_ws(separator,string1,string2,... ):连接括号内字符串或者数组,数量不限,连接符为separator。

    collect_set(字段):此函数只接受基本类型,主要是将字段的值进行去重汇总,产生array类型字段。

posted on 2020-05-16 14:29  hdc520  阅读(1715)  评论(0编辑  收藏  举报

导航