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

每个题目和每份试卷被作答的人数和次数

http://www.nowcoder.com/practice/203d0aed8928429a8978185d9a03babc

题意

给你一张试卷作答记录表,一张题目练习记录表,请你查询出其中每张试卷和每道题目被作答过的人数和作答的次数,最后按照人数和作答次数的顺序逆序排列

思路

  • 因为试卷和题目在不同的表中,而且两表中试卷和题目对应id列的字段名不同,所以需要我们分别查询出试卷和题目的作答结果并上下连接起来,所以需要使用到UNION,这里两个查询之间不需要去重,所以可以使用UNION ALL
  • 题目还要求我们要排序,但在UNION联合查询中,UNION只能出现在外面,不能出现在子连接查询中,因此两表的查询分别为

SQL1:

SELECT
    exam_id AS 'tid',
    COUNT(DISTINCT uid) AS 'uv',
    COUNT(exam_id) AS 'pv'
FROM
    exam_record
GROUP BY exam_id

SQL2:

SELECT
    question_id AS 'tid',
    COUNT(DISTINCT uid) AS 'uv',
    COUNT(question_id) AS 'pv'
FROM
    practice_record
GROUP BY question_id
  • 之后使用UNION ALL连接,最后再排序,SQL如下
(SQL1)
UNION ALL
(SQL2)
ORDER BY LEFT(tid, 1) DESC, uv DESC, pv DESC
  • 注意,连接后的查询是乱序的在对人数和作答次数排序之前,还需要对查询出的试卷/题目id进行排序

优化:

  • 通过查询计划可知,总消耗为8.20。两张表的访问方法都为全表扫描,且Extra中字段为Using filesort也就是文件排序,这是什么情况?我们没写ORDER BY呀?
  • 其实这是因为MySQL会在GROUP BY之后默认加上一个ORDER BY排序子句,且该查询在外层也显式地写了ORDER BY排序
  • 能不能将访问方法改为走覆盖索引呢?同样的,我们可以创建联合索引,在exam_record中的uid和exam_id字段上创建联合索引,在practice_record中的uid和question_id字段上创建联合索引
  • 但这样做其实于事无补,因为在排序时使用的是exam_id和question_id字段,而为了与查询列表一致,这两个字段都在联合索引的下一层,不能直接用于排序,再加上没有WHERE子句形成合适的扫描区间,所以此时我们的执行计划虽然走了覆盖索引但其效果和全表扫描差不多,且还是需要文件排序,大头开销并没有减少,JSON中的查询消耗也没变 alt alt

alt

  • 既然无法走覆盖索引意义不大,那么我们可以想办法将文件排序改为使用索引排序
  • 在exam_id和question_id上建立索引,再次查看执行计划,两张表的文件排序消失了!
  • 再次查看查询消耗,降为了2.20! alt alt
MySQL练习 文章被收录于专栏

解析牛客网中的SQL题目

全部评论
请问 order by LEFT(tid,1) DESC 这里的left为什么这样用呀,相当于是按9、8来排序吗?如果试卷id和题目id没有这样的固定特征,这样排序还对吗?
6 回复 分享
发布于 2022-01-12 22:54
此题中“试卷”是以9开头,“题目”是以8开头,所以你用left(tid,1)来排序,如果“试卷”和“题目”无此特征,该如何排序呢?烦请大佬解惑,谢谢
3 回复 分享
发布于 2022-01-13 14:17
大佬太强了
点赞 回复 分享
发布于 2021-12-29 11:17
你好,请问COUNT(DISTINCT uid) AS 'uv'是不是没有考虑是否提交呀。 如果exam_record第6行uid是9003的话COUNT(DISTINCT uid)是不是就不行了呢 应该要怎么改比较好呢。望指导。
点赞 回复 分享
发布于 2022-02-10 17:52
降低消耗那在哪?不是通过SQL语句来实现的吗?
点赞 回复 分享
发布于 2022-02-28 16:41

相关推荐

11-04 14:10
东南大学 Java
_可乐多加冰_:去市公司包卖卡的
点赞 评论 收藏
分享
25 4 评论
分享
牛客网
牛客企业服务