SQL31 未完成率较高的50%用户近三个月答卷情况
题目主要信息:
- 统计SQL试卷上未完成率较高的50%用户中,6级和7级用户在有试卷作答记录的近三个月中,每个月的答卷数目和完成数目
- 筛选的时候用户按未完成率降序排名,相同情况按uid降序
问题拆分:
- 先筛选出每个用户的ID、未完成的试卷数、总试卷数:
- 要计算每个用户的信息,因此要以uid分组。知识点:group by
- 用户ID直接选择,每个人总试卷数统计每个人的开始时间出现的次数。知识点:count()
- 每个用户的未完成的试卷数,我们统计得分,如果得分为null记为1,否则记为0,将每个人的这个if值求和就是得分为null出现的次数即每个人未完成试卷的份数。
sum(if(score is null, 1, 0)) as incomplete_cnt
知识点:if()、sum() - 筛选出的信息记为incomplete_cnt_table
- 从incomplete_cnt_table中筛选出每个用户ID、未完成率排名:
- incomplete_cnt_table已经分组了,所以是每个ID对应一份数据,直接选出ID,不用分组。
- 利用上述筛选出的每人的未完成试卷数和总试卷数计算未完成率,然后以先未完成率降序、再uid降序排名。
row_number() over(order by (incomplete_cnt / total_cnt) desc, uid desc) as incomplete_rank
知识点:row_number()over()、order by - 筛选出的信息记为incomplete_rate_table
- 对exam_record表中的不同的uid计数,统计总共多少用户做了试卷,计数信息记为表t_u。
select count(distinct uid) as total_user from exam_record
知识点:distinct、count() - 因为我们需要用户等级信息、总人数信息及未完成率排名,因此要将incomplete_rate_table与t_u直接连接在一起,然后通过uid再与user_info连接在一起。知识点:join
- 筛选出未完成率较高的50%及用户等级为6或7级的用户ID:
- 从三个连接的表中选出用户ID,条件是level>=6,并且排名小于等于总人数除2的向上取整(按照题目的意思,奇数人数要取中间因此向上取整)。
where level >= 6 and incomplete_rank <= ceiling(total_user / 2)
知识点:where、ceiling()
- 从exam_record中筛选出用户ID、开始做试卷时间、得分及做题月份的降序排名:
- 用户ID、开始做题时间、得分可以直接得到
- 做题月份的降序排名我们使用dense_rank()over(),因为可能会出现重复月份,我们要的是最大的三个月份,排名以uid分组,统计每个人的月份排名。
dense_rank() over(partition by uid order by date_format(start_time, '%Y%m') desc) as recent_months
知识点:dense_rank() over()、partition by、order by、date_format() - 筛选出的结果记为recent_table
- 从recent_table中筛选出满足条件的用户ID及及其最近三个月的做题情况:
- 需要统计每个用户每个月的情况,因此要以uid和start_month为分组。知识点:group by
- 对于每个分组统计完成总题数,直接对recent_table中的开始做题时间计数;统计完成的题目数,直接对recent_table中的得分次数做计数。 知识点:count()
- 当然筛选还有条件,就是月份排名小于等于3,即最近三个月,然后uid要位于上述筛选出来的前50%的uid中。知识点:where...and ... in...
- 最后按照用户ID排序输出即可。知识点:group by
代码:
select uid,
date_format(start_time, '%Y%m') as start_month,
count(start_time) as tatol_cnt,
count(score) as complete_cnt
from(
select uid, start_time, score,
dense_rank() over(partition by uid order by date_format(start_time, '%Y%m') desc) as recent_months
from exam_record
) recent_table
where recent_months <= 3
and uid in(
select incomplete_rate_table.uid
from(
select uid,
row_number() over(order by (incomplete_cnt / total_cnt) desc, uid desc) as incomplete_rank
from(
select uid,
sum(if(score is null, 1, 0)) as incomplete_cnt,
count(start_time) as total_cnt
from exam_record
group by uid
) incomplete_cnt_table
) incomplete_rate_table join(
select count(distinct uid) as total_user
from exam_record
) t_u
join user_info
on incomplete_rate_table.uid = user_info.uid
where level >= 6
and incomplete_rank <= ceiling(total_user / 2)
)
group by uid, start_month
order by uid