返回介绍

连接查询

发布于 2024-08-17 15:49:00 字数 14680 浏览 0 评论 0 收藏 0

满足条件的用户的试卷完成数和题目练习数(困难)

描述

现有用户信息表 user_info(uid 用户 ID,nick_name 昵称, achievement 成就值, level 等级, job 职业方向, register_time 注册时间):

iduidnick_nameachievementleveljobregister_time
11001牛客 1 号31007算法2020-01-01 10:00:00
21002牛客 2 号23007算法2020-01-01 10:00:00
31003牛客 3 号25007算法2020-01-01 10:00:00
41004牛客 4 号12005算法2020-01-01 10:00:00
51005牛客 5 号16006C++2020-01-01 10:00:00
61006牛客 6 号20006C++2020-01-01 10:00:00

试卷信息表 examination_info(exam_id 试卷 ID, tag 试卷类别, difficulty 试卷难度, duration 考试时长, release_time 发布时间):

idexam_idtagdifficultydurationrelease_time
19001SQLhard602021-09-01 06:00:00
29002C++hard602021-09-01 06:00:00
39003算法medium802021-09-01 10:00:00

试卷作答记录表 exam_record(uid 用户 ID, exam_id 试卷 ID, start_time 开始作答时间, submit_time 交卷时间, score 得分):

iduidexam_idstart_timesubmit_timescore
1100190012021-09-01 09:01:012021-09-01 09:31:0081
2100290022021-09-01 12:01:012021-09-01 12:31:0181
3100390012021-09-01 19:01:012021-09-01 19:40:0186
4100390022021-09-01 12:01:012021-09-01 12:31:5189
5100490012021-09-01 19:01:012021-09-01 19:30:0185
6100590022021-09-01 12:01:012021-09-01 12:31:0285
7100690032021-09-07 10:01:012021-09-07 10:21:0184
8100690012021-09-07 10:01:012021-09-07 10:21:0180

题目练习记录表 practice_record(uid 用户 ID, question_id 题目 ID, submit_time 提交时间, score 得分):

iduidquestion_idsubmit_timescore
1100180012021-08-02 11:41:0160
2100280012021-09-02 19:30:0150
3100280012021-09-02 19:20:0170
4100280022021-09-02 19:38:0170
5100480012021-08-02 19:38:0170
6100480022021-08-02 19:48:0190
7100180022021-08-02 19:38:0170
8100480022021-08-02 19:48:0190
9100480022021-08-02 19:58:0194
10100480032021-08-02 19:38:0170
11100480032021-08-02 19:48:0190
12100480032021-08-01 19:38:0180

请你找到高难度 SQL 试卷得分平均值大于 80 并且是 7 级的红名大佬,统计他们的 2021 年试卷总完成次数和题目总练习次数,只保留 2021 年有试卷完成记录的用户。结果按试卷完成数升序,按题目练习数降序。

示例数据输出如下:

uidexam_cntquestion_cnt
100112
100320

解释:用户 1001、1003、1004、1006 满足高难度 SQL 试卷得分平均值大于 80,但只有 1001、1003 是 7 级红名大佬;1001 完成了 1 次试卷 1001,练习了 2 次题目;1003 完成了 2 次试卷 9001、9002,未练习题目(因此计数为 0)

思路:

先将条件进行初步筛选,比如先查出做过高难度 sql 试卷的用户

SELECT
    record.uid
FROM
    exam_record record
    INNER JOIN examination_info e_info ON record.exam_id = e_info.exam_id
    JOIN user_info u_info ON record.uid = u_info.uid
WHERE
    e_info.tag = 'SQL'
    AND e_info.difficulty = 'hard'

然后根据题目要求,接着再往里叠条件即可;

但是这里又要注意:

第一:不能 YEAR(submit_time)= 2021 这个条件放到最后,要在 ON 条件里,因为左连接存在返回左表全部行,右表为 null 的情形,放在 JOIN 条件的 ON 子句中的目的是为了确保在连接两个表时,只有满足年份条件的记录会进行连接。这样可以避免其他年份的记录被包含在结果中。即 1001 做过 2021 年的试卷,但没有练习过,如果把条件放到最后,就会排除掉这种情况。

第二,必须是 COUNT(distinct er.exam_id) exam_cnt, COUNT(distinct pr.id) question_cnt, 要加 distinct,因为有左连接产生很多重复值。

答案

SELECT er.uid AS UID,
       count(DISTINCT er.exam_id) AS exam_cnt,
       count(DISTINCT pr.id) AS question_cnt
FROM exam_record er
LEFT JOIN practice_record pr ON er.uid = pr.uid
AND YEAR (er.submit_time)= 2021
AND YEAR (pr.submit_time)= 2021
WHERE er.uid IN
    (SELECT er.uid
     FROM exam_record er
     LEFT JOIN examination_info ei ON er.exam_id = ei.exam_id
     LEFT JOIN user_info ui ON er.uid = ui.uid
     WHERE tag = 'SQL'
       AND difficulty = 'hard'
       AND LEVEL = 7
     GROUP BY er.uid
     HAVING avg(score) > 80)
GROUP BY er.uid
ORDER BY exam_cnt,
         question_cnt DESC

可能细心的小伙伴会发现,为什么明明将条件限制了 tag = 'SQL' AND difficulty = 'hard' ,但是用户 1003 仍然能查出两条考试记录,其中一条的考试 tagC++ ; 这是由于 LEFT JOIN 的特性,即使没有与右表匹配的行,左表的所有记录仍然会被保留。

每个 6/7 级用户活跃情况(困难)

描述

现有用户信息表 user_infouid 用户 ID, nick_name 昵称, achievement 成就值, level 等级, job 职业方向, register_time 注册时间):

iduidnick_nameachievementleveljobregister_time
11001牛客 1 号31007算法2020-01-01 10:00:00
21002牛客 2 号23007算法2020-01-01 10:00:00
31003牛客 3 号25007算法2020-01-01 10:00:00
41004牛客 4 号12005算法2020-01-01 10:00:00
51005牛客 5 号16006C++2020-01-01 10:00:00
61006牛客 6 号26007C++2020-01-01 10:00:00

试卷信息表 examination_infoexam_id 试卷 ID, tag 试卷类别, difficulty 试卷难度, duration 考试时长, release_time 发布时间):

idexam_idtagdifficultydurationrelease_time
19001SQLhard602021-09-01 06:00:00
29002C++easy602021-09-01 06:00:00
39003算法medium802021-09-01 10:00:00

试卷作答记录表 exam_recorduid 用户 ID, exam_id 试卷 ID, start_time 开始作答时间, submit_time 交卷时间, score 得分):

uidexam_idstart_timesubmit_timescore
100190012021-09-01 09:01:012021-09-01 09:31:0078
100190012021-09-01 09:01:012021-09-01 09:31:0081
100590012021-09-01 19:01:012021-09-01 19:30:0185
100590022021-09-01 12:01:012021-09-01 12:31:0285
100690032021-09-07 10:01:012021-09-07 10:21:5984
100690012021-09-07 10:01:012021-09-07 10:21:0181
100290012020-09-01 13:01:012020-09-01 13:41:0181
100590012021-09-01 14:01:01(NULL)(NULL)

题目练习记录表 practice_recorduid 用户 ID, question_id 题目 ID, submit_time 提交时间, score 得分):

uidquestion_idsubmit_timescore
100180012021-08-02 11:41:0160
100480012021-08-02 19:38:0170
100480022021-08-02 19:48:0190
100180022021-08-02 19:38:0170
100480022021-08-02 19:48:0190
100680022021-08-04 19:58:0194
100680032021-08-03 19:38:0170
100680032021-08-02 19:48:0190
100680032020-08-01 19:38:0180

请统计每个 6/7 级用户总活跃月份数、2021 年活跃天数、2021 年试卷作答活跃天数、2021 年答题活跃天数,按照总活跃月份数、2021 年活跃天数降序排序。由示例数据结果输出如下:

uidact_month_totalact_days_2021act_days_2021_exam
1006341
1001221
1005111
1002100
1003000

解释 :6/7 级用户共有 5 个,其中 1006 在 202109、202108、202008 共 3 个月活跃过,2021 年活跃的日期有 20210907、20210804、20210803、20210802 共 4 天,2021 年在试卷作答区 20210907 活跃 1 天,在题目练习区活跃了 3 天。

思路:

这题的关键在于 CASE WHEN THEN 的使用,不然要写很多的 left join 因为会产生很多的结果集。

CASE WHEN THEN 语句是一种条件表达式,用于在 SQL 中根据条件执行不同的操作或返回不同的结果。

语法结构如下:

CASE
    WHEN condition1 THEN result1
    WHEN condition2 THEN result2
    ...
    ELSE result
END

在这个结构中,可以根据需要添加多个 WHEN 子句,每个 WHEN 子句后面跟着一个条件(condition)和一个结果(result)。条件可以是任何逻辑表达式,如果满足条件,将返回对应的结果。

最后的 ELSE 子句是可选的,用于指定当所有前面的条件都不满足时的默认返回结果。如果没有提供 ELSE 子句,则默认返回 NULL

例如:

SELECT score,
    CASE
        WHEN score >= 90 THEN '优秀'
        WHEN score >= 80 THEN '良好'
        WHEN score >= 60 THEN '及格'
        ELSE '不及格'
    END AS grade
FROM student_scores;

在上述示例中,根据学生成绩(score)的不同范围,使用 CASE WHEN THEN 语句返回相应的等级(grade)。如果成绩大于等于 90,则返回"优秀";如果成绩大于等于 80,则返回"良好";如果成绩大于等于 60,则返回"及格";否则返回"不及格"。

那了解到了上述的用法之后,回过头看看该题,要求列出不同的活跃天数。

count(distinct act_month) as act_month_total,
count(distinct case when year(act_time)='2021'then act_day end) as act_days_2021,
count(distinct case when year(act_time)='2021' and tag='exam' then act_day end) as act_days_2021_exam,
count(distinct case when year(act_time)='2021' and tag='question'then act_day end) as act_days_2021_question

这里的 tag 是先给标记,方便对查询进行区分,将考试和答题分开。

找出试卷作答区的用户

SELECT
        uid,
        exam_id AS ans_id,
        start_time AS act_time,
        date_format( start_time, '%Y%m' ) AS act_month,
        date_format( start_time, '%Y%m%d' ) AS act_day,
        'exam' AS tag
    FROM
        exam_record

紧接着就是答题作答区的用户

SELECT
        uid,
        question_id AS ans_id,
        submit_time AS act_time,
        date_format( submit_time, '%Y%m' ) AS act_month,
        date_format( submit_time, '%Y%m%d' ) AS act_day,
        'question' AS tag
    FROM
        practice_record

最后将两个结果进行 UNION 最后别忘了将结果进行排序 (这题有点类似于分治法的思想)

答案

SELECT user_info.uid,
       count(DISTINCT act_month) AS act_month_total,
       count(DISTINCT CASE
                          WHEN YEAR (act_time)= '2021' THEN act_day
                      END) AS act_days_2021,
       count(DISTINCT CASE
                          WHEN YEAR (act_time)= '2021'
                               AND tag = 'exam' THEN act_day
                      END) AS act_days_2021_exam,
       count(DISTINCT CASE
                          WHEN YEAR (act_time)= '2021'
                               AND tag = 'question' THEN act_day
                      END) AS act_days_2021_question
FROM
  (SELECT UID,
          exam_id AS ans_id,
          start_time AS act_time,
          date_format(start_time, '%Y%m') AS act_month,
          date_format(start_time, '%Y%m%d') AS act_day,
          'exam' AS tag
   FROM exam_record
   UNION ALL SELECT UID,
                    question_id AS ans_id,
                    submit_time AS act_time,
                    date_format(submit_time, '%Y%m') AS act_month,
                    date_format(submit_time, '%Y%m%d') AS act_day,
                    'question' AS tag
   FROM practice_record) total
RIGHT JOIN user_info ON total.uid = user_info.uid
WHERE user_info.LEVEL IN (6,
                          7)
GROUP BY user_info.uid
ORDER BY act_month_total DESC,
         act_days_2021 DESC

较难或者困难的题目可以根据自身实际情况和面试需要来决定是否要跳过。

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。
列表为空,暂无数据
    我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
    原文