题解 /优化!| #每个题目和每份试卷被作答的人数和次数#

分别满足两个活动的人

http://www.nowcoder.com/practice/a126cea91d7045e399b8ecdcadfb326f

题意:

给你一张试卷作答记录表,一张试卷信息表,请你查询出其中满足条件1或者条件2的用户id,其中条件1为:2021年所有试卷得分大于等于85分,条件2为至少有一次用时只占规定的一半就完成了高难度试卷,且分数大于80

思路:

  • 因为有两个条件,且查询的字段都是uid,所以我们需要查询两次之后将结果联合起来,需要使用UNION ALL(这里不需要去重)
  • 对于条件1,所有试卷都大于等于85则说明其最小的分数值也大于等于85,限制年份则使用YAER即可,注意我们获取最小分数需要使用MIN,则在获取每个用户对应的最小分数前需要分组,所以我们需要分组后再查询,所以需要使用HAVING而不是WHERE,且需要在GROUP BY之后,SQL如下

SQL1:

SELECT
	uid,
	'activity1' AS 'activity' 
FROM
	exam_record 
WHERE YEAR ( start_time ) = 2021 
GROUP BY uid 
HAVING MIN( score ) >= 85 
  • 接下来查询满足条件2的uid,首先高难度试卷其实就是试卷的难度为hard,而规定时间的一半其实就是试卷信息表中duration的一半,所以需要连接两表
  • 获取时间值的差值可以使用TIMESTAMPDIFF,并指定单位为SECOND即可,最后再指定分数和日期即可,SQL如下

SQL2:

SELECT 
	DISTINCT t1.uid,
	'activity2' AS 'activity'
FROM
	exam_record AS t1
INNER JOIN examination_info AS t2 ON t1.exam_id = t2.exam_id 
WHERE TIMESTAMPDIFF( SECOND, t1.start_time, t1.submit_time ) <= t2.duration * 30 
AND t2.difficulty = 'hard' AND score > 80 AND YEAR ( t1.start_time ) = 2021 
  • 获取两种情况后,再将这两个查询连接起来即可,SQL如下
SQL1
UNION ALL
SQL2
ORDER BY uid

优化:

  • 首先查看执行计划,此时JSON格式中的总开销为7.00 alt alt alt alt
  • 输出的信息中有四条记录,最后一条是联合查询时的临时表,因为我们最后写了ORDER BY排序,所以最后需要文件排序,且我们无法将该文件排序改为索引排序,因为排序的数据是连接后的记录,不是表中已有的记录,因此不考虑优化临时表
  • 内连接中,优化器最终选择将t2作为驱动表,而t2的限制条件比较多,所以连接后需要回表,因为涉及到的字段比较多,从空间开销的角度考虑就不创建联合索引了,就算创建了,其走覆盖索引的效率也和全表扫描差不了多少
  • t1表同理,所以我们暂时只需要考虑优化第一个查询
  • 从extra字段中可以看出,其使用了文件排序和临时表,这里我们就可以将文件排序转换为索引排序了,只需要在uid字段上创建一个索引即可,再次查看执行计划,文件排序和临时表都消失啦,消耗降为了2.00!
MySQL练习 文章被收录于专栏

解析牛客网中的SQL题目

全部评论
请问duration *30 其中*30是什么意思?你说的定时间的一半是uration的一半,不是应该除以,为什么是乘?
点赞 回复 分享
发布于 2022-03-26 09:59
换算 分钟换成秒钟就是乘以60 然后一半时间就是除以2 就是60/2=30
点赞 回复 分享
发布于 2022-04-09 16:52
我跟你一样在求activity2的uid时把时间<=30分钟,但是数据就多出一条1003 activity2 的数据。题目答案是不包含1003 activity2的。我觉得按照题目要求取一半时间是包含刚好等于一半时间的那条数据的,对吧
点赞 回复 分享
发布于 2022-04-10 22:24
你用啥软件写的sql
点赞 回复 分享
发布于 2023-02-23 20:46 江苏
在哪里体现的次数?
点赞 回复 分享
发布于 05-19 15:59 河北

相关推荐

废铁汽车人:秋招真是牛鬼蛇神齐聚一堂
点赞 评论 收藏
分享
尊嘟假嘟点击就送:加v细说,问题很大
点赞 评论 收藏
分享
7 1 评论
分享
牛客网
牛客企业服务