Sql练习记录

Hive窗口函数

题目:

第一道,有一个成绩表数据t1
uid	class_name score
A	Chinese		90
A	English		89
A	Math		88
B	...			...

求出总分并排序
uid	chinese_score	English_score	Math_score	total_score	rank
A	90				89				88			267			1
B	...
  • 行转列 + rank()函数
CREATE TABLE Person (
	uid varchar(255),
	class_name varchar(255),
	score int
);
INSERT INTO Person VALUES ("A", "Chinese", 90), ("A", 'Math', 100),("B", 'Chinese', 99),("B", 'Math', 100);


SELECT
    *,
    rank() over(order by total_score desc)
from(
    SELECT
        uid,
        max(
            case
                when class_name="Chinese" then score
                else 0
            end
        ) as chineses_score,
        max(
            case
                when class_name="Math" then score
                else 0
            end
        ) as math_score,
        sum(score) total_score
    
    FROM Person
    group by uid
) tmp;

  • 总结:
    1、行转列:转成多列,select 肯定要求出现这几列,直接写出来,然后case when
    2、rank() 函数 rank() over(partition by ... order by...)

  • 用户留存:去重,然后将日期-1,关联到一起说明是连续登录的那几天

求11月份的新用户次日留存率(牛客网题目)

id login_time 
1	2021-11-01 12:00:01	
2	2021-11-01 12:00:01	
3	2021-11-01 12:00:01	
1	2021-11-02 12:00:01	
2	2021-11-02 12:00:01	
4	2021-11-02 12:00:01	
...
比如这张表的新用户留存率为66.7%
11.1 有三个新用户 1 2 3
11.2 两个新用户留下来了 1 2
所以新用户留存率为 2/3=66.7%

https://www.nowcoder.com/questionTerminal/126083961ae0415fbde061d7ebbde453?f=discussion

  • 总结
    1、date_sub(date, interval 1 day) 这个函数需要记住
    2、计算留存:用户聚合后,操作日期,两个表left join,能join的上的就是第二天、第三天登录的
select
    avg(if(b.device_id is not null,1,0)) as avg_ret
from
(
select
    distinct
    device_id,
    date
from
    question_practice_detail
)a 
left join(
select
    distinct
    device_id,
    date_sub(date, interval 1 day) as date
from
    question_practice_detail
)b on a.device_id = b.device_id and a.date = b.date

  • hive实现:非北京市的学生中,每个班级 平均分最高的TO10用户及信息,
    结果格式为:class, uid, avg_score, scores,province
用户学分表Score:
字段名	中文名	字段类型	字段示例
uid	用户id	bigint	23145
class	班级	string	2-1
scores	用户信息	array<string>	['math_100','music_90',......]

用户信息表   stu
字段名	中文名	字段类型	字段示例
uid	用户id	bigint	23145
province	省份	string	北京

本题的核心就是将各科目成绩列举出来
配合 LATERAL VIEW、explode 2个函数,可以实现把一个array类型的值分开。
LATERAL VIEW通常用于一行转多行的处理逻辑。
在hive中实现多行转一行的处理逻辑可以使用<collect_set> <collect_list>函数来实现, 他们返回的对象都是一个array集合,那么又回到上面array对象的处理逻辑了,差别在在于前者会去重。concat(subject,'-',score),然后将结果collect_set

select
    uid,
    class
    temp_score
from 
    Score
lateral view
    explode(scores) v_temp as temp_score
;

-- 写到这,我们可以拿到
id class subject-score
1  1      math-90
1  1      music-100

然后怎么办?
select split('math_90', '\_')[0];

select
    *
from(

    select
       uid, 
       class,
       province,
       avg_score,
       sum_score,
       rank() over(partition by class order by avg_score desc) ranking
    from(
    
        select
            t.uid,
            t.class,
            t.province,
            avg(score) avg_score,
            sum(score) sum_score
        from(
          -- 到这 表正常,可以进行排名操作
            select
                a.uid,
                a.class,
                a.province,
                split(temp_score, '\_')[0] as subject,
                split(temp_score, '\_')[1] as score
            from(
                select
                    uid,
                    class,
                    province,
                    temp_score
                from 
                    Score
                join(
                    select
                        uid,
                        province
                    from
                        stu
                    where
                        province != '北京'
                ) pro on pro.uid = Score.uid
                lateral view
                    explode(scores) v_temp as temp_score
            )a
        )t
        group by t.uid, t.class,t.province
        
    )scores
    
)rank_temp
where rank_temp.ranking <= 10;

总结:
1、通过explode 将array_string 炸成多行,借助lateral view
关联到对应的学生上,由此表变成
【学生 班级 科目 成绩 省份】
2、取排名,需要先算出平均分,套一层基于平均分,再最后一行追加上班级排名
3、再套一层,where 筛掉 ranking <= 10


总结:
1、get_json_object -- 输入字符串,输出指定key的值get_json_object('{"movie":"594","rate":"4","timeStamp":"978302268","uid":"1"}','$.movie')

2、json_tuple -- 可以做到将字符串一次性提取多个key

select b.b_movie,b.b_rate,b.b_timeStamp,b.b_uid from json a 
lateral view json_tuple(a.data,'movie','rate','timeStamp','uid') b as b_movie,b_rate,b_timeStamp,b_uid;

但如果给的是json数组,就不管用, 像这种 [{},{}],咋办?
explode 将数组,炸成多行,但一般给的都是字符串。。。
1、regexp_replace(string, '\[|\]', '') 先将字符串[]去掉
2、regexp_replace 再将{},{} 之间的,换成; 避免split勿切割
3、split(string, ';') -- 产出一个个'{}' 的数组
4、explode上场,将其炸成多行

SELECT explode(split(
    regexp_replace(
        regexp_replace(
            '[
                {"website":"www.baidu.com","name":"百度"},
                {"website":"google.com","name":"谷歌"}
            ]', 
            '\\[|\\]',''),  --将 Json 数组两边的中括号去掉
            
                 '\\}\\,\\{'    --将 Json 数组元素之间的逗号换成分号
                ,'\\}\\;\\{'),
                
                 '\\;'));    --以分号作为分隔符
posted @ 2022-07-16 10:05  spongie  阅读(21)  评论(0编辑  收藏  举报