题解 | #未完成试卷数大于1的有效用户#

未完成试卷数大于1的有效用户

https://www.nowcoder.com/practice/46cb7a33f7204f3ba7f6536d2fc04286

题目:

请统计2021年每个未完成试卷作答数大于1的有效用户的数据(有效用户指完成试卷作答数至少为1且未完成数小于5),
输出用户ID、未完成试卷作答数、完成试卷作答数、作答过的试卷tag集合,结果按未完成试卷数量由多到少排序。

  • 注意,题目还有一点要求其实没讲清楚,但在结果中有要求到:在字段“detail”中的“{日期:tag}集合”,也需要按照日期升序来排序:比如,必须要是“2021-07-02:SQL;2021-07-05:SQL;2021-09-01:算法;2021-09-02:SQL;2021-09-05:SQL”

  • 另外,还有一个大雷点是,本题目存在“submit_time”为NULL但依旧存在score有数的情况,因此,在计算未完成数时,我会使用submit_time来做


所用新函数:GROUP_CONCAT()

  • 参考易百教程 ,GROUP_CONCAT()函数将组中的字符串连接成为具有各种选项的单个字符串。下面说明了GROUP_CONCAT()函数的语法:
    GROUP_CONCAT(DISTINCT expression
                 ORDER BY expression
                 SEPARATOR sep);
  • 比如说,我想要根据字段“submit_time”中多行的值都合并为一个值,每个值间使用分号“;”间隔开,并且按照submit_time的时间升序来合并,那我就可以写成:
    GROUP_CONCAT(DISTINCT submit_time
                 ORDER BY submit_time ASC
                 SEPARATOR ";");

解题思路

步骤 1:从简单的先开始,先计算每个用户未完成数和完成数,并找出有效用户

  • 老生常谈了,不赘述,直接上代码:
    SELECT er.uid,
          COUNT(CASE WHEN er.submit_time IS NULL THEN 1 ELSE NULL END) AS incomplete_cnt,
          COUNT(er.submit_time) AS complete_cnt
    FROM exam_record AS er LEFT OUTER JOIN examination_info AS ei
    ON er.exam_id = ei.exam_id
    WHERE YEAR(er.start_time) = 2021
    GROUP BY er.uid
    /* 找出未完成试卷作答数大于1的有效用户 */
    HAVING complete_cnt >= 1 AND (incomplete_cnt BETWEEN 2 AND 4)  

步骤 2:针对作答过的试卷集合(即detail字段),需要先整合出“{日期:tag}”这一新字段,再根据UID分组去合并该字段下的不同行

  • 注意,这里还需要DISTINCT去重,即,若存在同一天做了同一个类型试卷多次,则只需要取一个即可;比如说“2021-09-05:算法;2021-09-05:算法” --> “2021-09-05:算法”
  • 另外,合并的时候,也需要按照日期升序
  • 先看第一小步,整合出新字段“{日期:tag}”:
    SELECT er.uid,
           /* 整合新字段,只需要使用CONCAT函数,让start_time和 */
           CONCAT(DATE(er.start_time), ":", ei.tag) 
    FROM exam_record AS er LEFT OUTER JOIN examination_info AS ei
    ON er.exam_id = ei.exam_id
    WHERE YEAR(er.start_time) = 2021
  • 第二小步,要根据UID分组之后,把UID对应的“{日期:tag}”行都再次整合起来,这里就需要用到GROUP_CONCT()函数:
    SELECT er.uid,
           /* 对“{日期:tag}”去重,并按照日期升序(这里直接按照新字段升序即可,因为日期在前半部分);
               最后再用分号“;”把它们分分隔开 */
           GROUP_CONCAT(DISTINCT CONCAT(DATE(er.start_time), ":", ei.tag) 
                        ORDER BY CONCAT(DATE(er.start_time), ":", ei.tag) ASC
                        SEPARATOR ";") AS detail
    FROM exam_record AS er LEFT OUTER JOIN examination_info AS ei
    ON er.exam_id = ei.exam_id
    WHERE YEAR(er.start_time) = 2021
    GROUP BY er.uid

步骤 3:可以看到步骤2和1其实是可以合并到一起的,最终得出来的就是题目所需

  • 不知是否有同学会有疑问说,步骤1明明和步骤2还有个不同:步骤1中会用HAVING这一筛选条件 —— 其实该条件并不影响步骤2的detail产出,因为HAVING是在得出分组结果之后,基于分组结果再筛选/行数删减的;即,步骤2的detail字段是在分组得出的结果,而HAVING是无法再对其造成影响的,只是删减了原有分组结果的行数
    SELECT er.uid,
          COUNT(CASE WHEN er.submit_time IS NULL THEN 1 ELSE NULL END) AS incomplete_cnt,
          COUNT(er.submit_time) AS complete_cnt,
          GROUP_CONCAT(DISTINCT CONCAT(DATE(er.start_time), ":", ei.tag) 
                       ORDER BY CONCAT(DATE(er.start_time), ":", ei.tag) ASC
                       SEPARATOR ";") AS detail
    FROM exam_record AS er LEFT OUTER JOIN examination_info AS ei
    ON er.exam_id = ei.exam_id
    WHERE YEAR(er.start_time) = 2021
    GROUP BY er.uid
    HAVING complete_cnt >= 1 AND (incomplete_cnt BETWEEN 2 AND 4)
    ORDER BY incomplete_cnt DESC;
全部评论

相关推荐

用户64975461947315:这不很正常吗,2个月开实习证明,这个薪资也还算合理,深圳Java好多150不包吃不包住呢,而且也提前和你说了没有转正机会,现在贼多牛马公司骗你说毕业转正,你辛辛苦苦干了半年拿到毕业证,后面和你说没hc了😂
点赞 评论 收藏
分享
评论
点赞
收藏
分享

创作者周榜

更多
牛客网
牛客企业服务