合并查询
每个题目和每份试卷被作答的人数和次数
描述 :
现有试卷作答记录表 exam_record(uid 用户 ID, exam_id 试卷 ID, start_time 开始作答时间, submit_time 交卷时间, score 得分):
id | uid | exam_id | start_time | submit_time | score |
---|---|---|---|---|---|
1 | 1001 | 9001 | 2021-09-01 09:01:01 | 2021-09-01 09:41:01 | 81 |
2 | 1002 | 9002 | 2021-09-01 12:01:01 | 2021-09-01 12:31:01 | 70 |
3 | 1002 | 9001 | 2021-09-01 19:01:01 | 2021-09-01 19:40:01 | 80 |
4 | 1002 | 9002 | 2021-09-01 12:01:01 | 2021-09-01 12:31:01 | 70 |
5 | 1004 | 9001 | 2021-09-01 19:01:01 | 2021-09-01 19:40:01 | 85 |
6 | 1002 | 9002 | 2021-09-01 12:01:01 | (NULL) | (NULL) |
题目练习表 practice_record(uid 用户 ID, question_id 题目 ID, submit_time 提交时间, score 得分):
id | uid | question_id | submit_time | score |
---|---|---|---|---|
1 | 1001 | 8001 | 2021-08-02 11:41:01 | 60 |
2 | 1002 | 8001 | 2021-09-02 19:30:01 | 50 |
3 | 1002 | 8001 | 2021-09-02 19:20:01 | 70 |
4 | 1002 | 8002 | 2021-09-02 19:38:01 | 70 |
5 | 1003 | 8001 | 2021-08-02 19:38:01 | 70 |
6 | 1003 | 8001 | 2021-08-02 19:48:01 | 90 |
7 | 1003 | 8002 | 2021-08-01 19:38:01 | 80 |
请统计每个题目和每份试卷被作答的人数和次数,分别按照"试卷"和"题目"的 uv & pv 降序显示,示例数据结果输出如下:
tid | uv | pv |
---|---|---|
9001 | 3 | 3 |
9002 | 1 | 3 |
8001 | 3 | 5 |
8002 | 2 | 2 |
解释 :“试卷”有 3 人共练习 3 次试卷 9001,1 人作答 3 次 9002;“刷题”有 3 人刷 5 次 8001,有 2 人刷 2 次 8002
思路 :这题的难点和易错点在于 UNOIN
和 ORDER BY
同时使用的问题
有以下几种情况:使用 union
和多个 order by
不加括号,报错!
order by
在 union
连接的子句中不起作用;
比如不加括号:
SELECT exam_id AS tid,
COUNT(DISTINCT UID) AS uv,
COUNT(UID) AS pv
FROM exam_record
GROUP BY exam_id
ORDER BY uv DESC,
pv DESC
UNION
SELECT question_id AS tid,
COUNT(DISTINCT UID) AS uv,
COUNT(UID) AS pv
FROM practice_record
GROUP BY question_id
ORDER BY uv DESC,
pv DESC
直接报语法错误,如果没有括号,只能有一个 order by
还有一种 order by
不起作用的情况,但是能在子句的子句中起作用,这里的解决方案就是在外面再套一层查询。
答案 :
SELECT *
FROM
(SELECT exam_id AS tid,
COUNT(DISTINCT exam_record.uid) uv,
COUNT(*) pv
FROM exam_record
GROUP BY exam_id
ORDER BY uv DESC, pv DESC) t1
UNION
SELECT *
FROM
(SELECT question_id AS tid,
COUNT(DISTINCT practice_record.uid) uv,
COUNT(*) pv
FROM practice_record
GROUP BY question_id
ORDER BY uv DESC, pv DESC) t2;
分别满足两个活动的人
描述 : 为了促进更多用户在牛客平台学习和刷题进步,我们会经常给一些既活跃又表现不错的用户发放福利。假使以前我们有两拨运营活动,分别给每次试卷得分都能到 85 分的人(activity1)、至少有一次用了一半时间就完成高难度试卷且分数大于 80 的人(activity2)发了福利券。
现在,需要你一次性将这两个活动满足的人筛选出来,交给运营同学。请写出一个 SQL 实现:输出 2021 年里,所有每次试卷得分都能到 85 分的人以及至少有一次用了一半时间就完成高难度试卷且分数大于 80 的人的 id 和活动号,按用户 ID 排序输出。
现有试卷信息表 examination_info
( exam_id
试卷 ID, tag
试卷类别, difficulty
试卷难度, duration
考试时长, release_time
发布时间):
id | exam_id | tag | difficulty | duration | release_time |
---|---|---|---|---|---|
1 | 9001 | SQL | hard | 60 | 2021-09-01 06:00:00 |
2 | 9002 | C++ | easy | 60 | 2021-09-01 06:00:00 |
3 | 9003 | 算法 | medium | 80 | 2021-09-01 10:00:00 |
试卷作答记录表 exam_record
( uid
用户 ID, exam_id
试卷 ID, start_time
开始作答时间, submit_time
交卷时间, score
得分):
id | uid | exam_id | start_time | submit_time | score |
---|---|---|---|---|---|
1 | 1001 | 9001 | 2021-09-01 09:01:01 | 2021-09-01 09:31:00 | 81 |
2 | 1002 | 9002 | 2021-09-01 12:01:01 | 2021-09-01 12:31:01 | 70 |
3 | 1003 | 9001 | 2021-09-01 19:01:01 | 2021-09-01 19:40:01 | 86 |
4 | 1003 | 9002 | 2021-09-01 12:01:01 | 2021-09-01 12:31:01 | 89 |
5 | 1004 | 9001 | 2021-09-01 19:01:01 | 2021-09-01 19:30:01 | 85 |
示例数据输出结果:
uid | activity |
---|---|
1001 | activity2 |
1003 | activity1 |
1004 | activity1 |
1004 | activity2 |
解释 :用户 1001 最小分数 81 不满足活动 1,但 29 分 59 秒完成了 60 分钟长的试卷得分 81,满足活动 2;1003 最小分数 86 满足活动 1,完成时长都大于试卷时长的一半,不满足活动 2;用户 1004 刚好用了一半时间(30 分钟整)完成了试卷得分 85,满足活动 1 和活动 2。
思路 : 这一题需要涉及到时间的减法,需要用到 TIMESTAMPDIFF()
函数计算两个时间戳之间的分钟差值。
下面我们来看一下基本用法
示例:
TIMESTAMPDIFF(MINUTE, start_time, end_time)
TIMESTAMPDIFF()
函数的第一个参数是时间单位,这里我们选择 MINUTE
表示返回分钟差值。第二个参数是较早的时间戳,第三个参数是较晚的时间戳。函数会返回它们之间的分钟差值
了解了这个函数的用法之后,我们再回过头来看 activity1
的要求,求分数大于 85 即可,那我们还是先把这个写出来,后续思路就会清晰很多
SELECT DISTINCT UID
FROM exam_record
WHERE score >= 85
AND YEAR (start_time) = '2021'
根据条件 2,接着写出 在一半时间内完成高难度试卷且分数大于 80 的人
SELECT UID
FROM examination_info info
INNER JOIN exam_record record
WHERE info.exam_id = record.exam_id
AND (TIMESTAMPDIFF(MINUTE, start_time, submit_time)) < (info.duration / 2)
AND difficulty = 'hard'
AND score >= 80
然后再把两者 UNION
起来即可。(这里特别要注意括号问题和 order by
位置,具体用法在上一篇中已提及)
答案 :
SELECT DISTINCT UID UID,
'activity1' activity
FROM exam_record
WHERE UID not in
(SELECT UID
FROM exam_record
WHERE score<85
AND YEAR(submit_time) = 2021 )
UNION
SELECT DISTINCT UID UID,
'activity2' activity
FROM exam_record e_r
LEFT JOIN examination_info e_i ON e_r.exam_id = e_i.exam_id
WHERE YEAR(submit_time) = 2021
AND difficulty = 'hard'
AND TIMESTAMPDIFF(SECOND, start_time, submit_time) <= duration *30
AND score>80
ORDER BY UID
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论