一、知识点总结与拓展
1)对同一列数据进行加减操作
- 情况1可排序时:先排序并错位LEAD()OVER()
- 直接根据条件进行加减筛选SUM(IF(真,1,0))或者 SUM(CASE WHEN ... THEN...END),并使用聚合函数GROUP BY。
- 看到有别的小伙伴是这样直接计算的,做个记录:
SELECT #第一步:找到所有tag在2020,2021上半年的完成数 tag, SUM(IF(DATE_FORMAT(submit_time, '%Y-%m') BETWEEN '2020-01' AND '2020-06', 1, 0)) exam_cnt_20, SUM(IF(DATE_FORMAT(submit_time, '%Y-%m') BETWEEN '2021-01' AND '2021-06', 1, 0)) exam_cnt_21 FROM exam_record LEFT JOIN examination_info ei USING(exam_id) GROUP BY 1)
2)百分比格式表示
CONCAT(字段名,'%')
3)数据格式转换
- CAST(字段名 AS 格式类型 )
-
SQL常用的格式类型
- 二进制:BINARY
- 字符型: CHAR
- 日期 : DATE
- 时间: TIME
- 日期时间型 : DATETIME
- 浮点数 : DECIMAL
- 整数 : SIGNED
- 无符号整数 : UNSIGNED
二、题目解读与解题步骤拆分
1、题目解读
求:请计算2021年上半年各类试卷的做完次数相比2020年上半年同期的增长率(百分比格式,保留1位小数),以及做完次数排名变化,按增长率和21年排名降序输出。
坑1:增长率的计算:(2021做完次数-2020做完次数)/2020做完次数
坑2:做完次数排名变化:2021排名-2020排名
坑3:排名方式:根据解释中2020年的排名1 1 3 可知,选择rank( )over( )函数处理排名
需求字段:
- tag 试卷名
- exam_cnt_20 2020年上半年做完次数
- exam_cnt_21 2021年上半年做完次数
- growth_rate 增长率
- exam_cnt_rank_20 2020年上半年做完次数排名
- exam_cnt_rank_21 2021年上半年做完次数排名
- rank_delta 排名变化(eg.后退1名)
2、解题步骤拆分
1)请计算2021年上半年
- 2020和2021上半年的数据范围圈定
2)各类试卷的做完次数
- 按照年份和类别,对各类试卷上半年的完成情况进行聚类:COUNT(score) AS exam_cnt_20/21
3)相比2020年上半年同期的增长率(百分比格式,保留1位小数)
- 增长率=(2021上半年你做完次数-2020上半年做完次数)/2020上半年做完次数
- (exam_cnt_21 - exam_cnt_20)/exam_cnt_20 AS growth_rate
- 小数点保留1位小数ROUND(growth_rate,1)
4)以及做完次数排名变化
- 对次数进行排名:RANK( )OVER( ) AS exam_cnt_rank_20/21
- 排名变化:exam_cnt_rank_21 - exam_cnt_rank_20
5)按增长率和21年排名降序输出。
三、步骤代码
1)2020和2021上半年的数据范围圈定
- MONTH(submit_time) between 1 AND 6 -- 上半年数据
-
submit_time BETWEEN '2020-01-00 00:00:00' AND '2022-01-01 00:00:00' -- 选取2020和2021年的数据
2)按照年份和类别,对各类试卷上半年的完成情况进行聚类,并进行排名
- RANK()OVER(PARTITION BY YEAR(submit_time) ORDER BY COUNT(score) DESC -- 分别对2021和2020年的做题情况进行排名
-
YEAR(submit_time) 、 COUNT(score)这俩是聚合函数数据,所以要配合 GROUP BY 使用
SELECT exam_id,YEAR(submit_time) start_year ,COUNT(score) exam_cnt,RANK()OVER(PARTITION BY YEAR(submit_time) ORDER BY COUNT(score) DESC) rk /*分别对2021和2020的做完情况进行排名*/ FROM exam_record WHERE MONTH(submit_time) BETWEEN 1 AND 6 -- 选取下半年数据 AND submit_time BETWEEN '2020-01-00 00:00:00' AND '2022-01-01 00:00:00' -- 选取2020和2021年的数据 GROUP BY YEAR(submit_time),exam_id /*对年份和类别进行聚类*/ ;
3)增长率=(2021上半年你做完次数-2020上半年做完次数)/2020上半年做完次数
(exam_cnt_21 - exam_cnt_20)/exam_cnt_20 AS growth_rate
- 将2020年和2021年的完成次数和排名数据分开
SELECT exam_id,IF(start_year='2020',exam_cnt,NULL)exam_cnt_20,-- 2020年的完成次数 LEAD(exam_cnt,1)OVER(PARTITION BY exam_id ORDER BY start_year) exam_cnt_21,-- 2021年的完成次数 IF(start_year='2020',rk,NULL)exam_cnt_rank_20,-- 2020年的排名 LEAD(rk,1)OVER(PARTITION BY exam_id ORDER BY start_year) exam_cnt_rank_21 -- 2021年的排名 FROM ( SELECT exam_id,YEAR(submit_time) start_year ,COUNT(score) exam_cnt,RANK()OVER(PARTITION BY YEAR(submit_time) ORDER BY COUNT(score) DESC) rk /*分别对2021和2020的做完情况进行排名*/ FROM exam_record WHERE MONTH(submit_time) BETWEEN 1 AND 6 -- 选取下半年数据 AND submit_time BETWEEN '2020-01-00 00:00:00' AND '2022-01-01 00:00:00' -- 选取2020和2021年的数据 GROUP BY YEAR(submit_time),exam_id /*对年份和类别进行聚类*/ )t1;
- 小数点保留1位小数ROUND(growth_rate,1)=ROUND((exam_cnt_21 - exam_cnt_20)/exam_cnt_20,1)
- 改成百分比格式:CONCAT(ROUND((exam_cnt_21 - exam_cnt_20)/exam_cnt_20*100,1),'%') AS growth_rate
5)排名变化
- exam_cnt_rank_21 - exam_cnt_rank_20
⚠️⚠️⚠️直接相减系统报错,对exam_cnt_rank_21 和 exam_cnt_rank_20的格式进行强行更改为整数。
- CAST(exam_cnt_rank_21 AS SIGNED ) - CAST(exam_cnt_rank_20 AS SIGNED ) AS rank_delta
7)按增长率和21年排名降序输出。
- ORDER BY growth_rate DESC,exam_cnt_rank_21 DESC
四、完整代码
WITH t2 AS( SELECT exam_id,IF(start_year='2020',exam_cnt,NULL)exam_cnt_20,-- 2020年的完成次数 LEAD(exam_cnt,1)OVER(PARTITION BY exam_id ORDER BY start_year) exam_cnt_21,-- 2021年的完成次数 IF(start_year='2020',rk,NULL)exam_cnt_rank_20,-- 2020年的排名 LEAD(rk,1)OVER(PARTITION BY exam_id ORDER BY start_year) exam_cnt_rank_21 -- 2021年的排名 FROM ( SELECT exam_id,YEAR(submit_time) start_year ,COUNT(score) exam_cnt,RANK()OVER(PARTITION BY YEAR(submit_time) ORDER BY COUNT(score) DESC) rk /*分别对2021和2020的做完情况进行排名*/ FROM exam_record WHERE MONTH(submit_time) BETWEEN 1 AND 6 -- 选取下半年数据 AND submit_time BETWEEN '2020-01-00 00:00:00' AND '2022-01-01 00:00:00' -- 选取2020和2021年的数据 GROUP BY YEAR(submit_time),exam_id /*对年份和类别进行聚类*/ )t1 ) SELECT a.tag, exam_cnt_20,exam_cnt_21, CONCAT(ROUND((exam_cnt_21-exam_cnt_20)*100/exam_cnt_20,1),'%')AS growth_rate, exam_cnt_rank_20,exam_cnt_rank_21, CAST(exam_cnt_rank_21 AS SIGNED)-CAST(exam_cnt_rank_20 AS SIGNED) AS rank_delta -- 需要转换格式,否则会报错 FROM t2 LEFT JOIN examination_info AS a ON a.exam_id=t2.exam_id WHERE exam_cnt_21 IS NOT NULL ORDER BY growth_rate DESC,exam_cnt_rank_21 DESC ;