• 【LeetCode高频SQL50题-基础版】打卡第8天:第41~45题


    【LeetCode高频SQL50题-基础版】打卡第8天:第41~45题

    ⛅前言

      在这个博客专栏中,我将为大家提供关于 LeetCode 高频 SQL 题目的基础版解析。LeetCode 是一个非常受欢迎的编程练习平台,其中的 SQL 题目涵盖了各种常见的数据库操作和查询任务。对于计算机科班出身的同学来说,SQL 是一个基础而又重要的技能。不仅在面试过程中经常会遇到 SQL 相关的考题,而且在日常的开发工作中,掌握 SQL 的能力也是必备的。

      本专栏的目的是帮助读者掌握 LeetCode 上的高频 SQL 题目,并提供对每个题目的解析和解决方案。我们将重点关注那些经常出现在面试中的题目,并提供一个基础版的解法,让读者更好地理解问题的本质和解题思路。无论你是准备找工作还是提升自己的技能,在这个专栏中,你可以学习到很多关于 SQL 的实践经验和技巧,从而更加深入地理解数据库的操作和优化。

      我希望通过这个专栏的分享,能够帮助读者在 SQL 的领域里取得更好的成绩和进步。如果你对这个话题感兴趣,那么就跟随我一起,开始我们的 LeetCode 高频 SQL 之旅吧!

    好友申请II

    🔒题目

    题目来源:602.好友申请II:谁有最多的好友

    image-20231014144055564

    🔑题解

    • 考察知识点子查询union all

    分析:我的思路是先分别统计 requester_id 和 accepter_id 这两列 id 出现的次数,然后将这两个统计结果使用 union all 聚合起来,基于这张聚合的临时表作最终的统计

    1)分别统计 requester_id 和 accepter_id,使用 union all 对结果进行一个合并

    注意:这里一定要使用 union all,而不是 union,因为 union 会直接将查询的结果进行去重,按照题目的含义,这里是不需要进行去重的,两列 id 出现的次数都需要进行统计的

    select requester_id id, count(*) num
    from RequestAccepted
    group by requester_id
    union all
    select accepter_id id, count(*) num
    from RequestAccepted
    group by accepter_id
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    | id | num |
    | -- | --- |
    | 1  | 2   |
    | 2  | 1   |
    | 3  | 1   |
    | 2  | 1   |
    | 3  | 2   |
    | 4  | 1   |
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    2)基于上面的表进行一个最终的结果获取

    这一部就相当简单, 可以直接对按照 id 进行一个分组,然后使用 sum 聚合函数统计每一个 id 出现的次数,最终在降序排序,limit截取第一个元素,即可筛选出最终的结果

    select id, sum(num) num
    from(
        select requester_id id, count(*) num
        from RequestAccepted
        group by requester_id
        union all
        select accepter_id id, count(*) num
        from RequestAccepted
        group by accepter_id
    ) t
    group by id
    order by num desc
    limit 1;
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    2016年的投资

    🔒题目

    题目来源:585.2016年的投资

    image-20231014150225342

    🔑题解

    • 考察知识点roundsum子查询group byinhavingcount

    分析:这一题题意还算十分清晰,我们只需要按照题目要求一步一步来即可,题目要求我们查询出满足下面两个条件的所有投保人的在2016年的总金额

    1. 在 2015 年的投保额 (tiv_2015) 至少跟一个其他投保人在 2015 年的投保额相同。
    2. 所在的城市必须与其他投保人都不同(也就是说 (lat, lon) 不能跟其他任何一个投保人完全相同)

    我的思路是,先查询出满足第一个条件的所有 tiv_2015,然后插叙出所有满足第二个条件的 lat 和 lon,再将这两个条件进行取一个交集,最周在利用 sum + round 函数对筛选出的记录进行一个求和,最后就可以得到最终的答案了

    1)查询出满足第一个条件的所有的 tiv_2015

    select tiv_2015
    from Insurance 
    group by tiv_2015
    having count(*) > 1
    
    • 1
    • 2
    • 3
    • 4
    | tiv_2015 |
    | -------- |
    | 10       |
    
    • 1
    • 2
    • 3

    2)查询出满足第二个条件的所有的 lat 和 lon

    这里可以巧妙的利用 group by进行两个字段的分组,可能平常我们都是使用一个字段进行分组,实际上 group by可以同时按照多个字段进行分组

    select lat, lon
    from Insurance
    group by lat, lon
    having count(*) = 1
    
    • 1
    • 2
    • 3
    • 4
    | lat | lon |
    | --- | --- |
    | 10  | 10  |
    | 40  | 40  |
    
    • 1
    • 2
    • 3
    • 4

    3)利用 in 和 and 使用上面两个结果集进行交集,然后筛选出符合所有条件的记录,最终通过 round 和 sum 进行最终的计算

    select round(sum(tiv_2016), 2) tiv_2016
    from Insurance
    where tiv_2015 in (
        select tiv_2015
        from Insurance 
        group by tiv_2015
        having count(*) > 1
    ) and (lat, lon) in (
        select lat, lon
        from Insurance
        group by lat, lon
        having count(*) = 1
    );
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    部门工资前三高的所有员工

    🔒题目

    题目来源:185.部门工资前三高的所有员工

    image-20231014153006689

    🔑题解

    • 考察知识点dense_rank子查询自连接countdistinct

    分析:这一题最开始我的第一反应就是使用窗口函数,因为这种分组排序使用窗口函数将十分的方便,具体的思路如下

    1. 确定使用哪一个窗口函数,这里可以明确使用 dense_rank 窗口函数(并列排序,不会跳过重复的序号 1、1、2)
    2. 剩下的就很好办的,我们只需要筛选出排名前三的即可

    1)利用窗口函数对所有的记进行员工分组排序

    select *, dense_rank() over(partition by d.name order by salary desc) ranking
    from Department d left join Employee e on d.id = e.departmentId
    
    • 1
    • 2
    | id | name  | id | name  | salary | departmentId | ranking |
    | -- | ----- | -- | ----- | ------ | ------------ | ------- |
    | 1  | IT    | 4  | Max   | 90000  | 1            | 1       |
    | 1  | IT    | 6  | Randy | 85000  | 1            | 2       |
    | 1  | IT    | 1  | Joe   | 85000  | 1            | 2       |
    | 1  | IT    | 7  | Will  | 70000  | 1            | 3       |
    | 1  | IT    | 5  | Janet | 69000  | 1            | 4       |
    | 2  | Sales | 2  | Henry | 80000  | 2            | 1       |
    | 2  | Sales | 3  | Sam   | 60000  | 2            | 2       |
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    2)筛选出排名前三的员工信息

    select  Department, Employee, salary
    from(
        select d.name Department, 
           e.name Employee,
           e.salary,
           dense_rank() over(partition by d.name order by salary desc) ranking
        from Department d left join Employee e on d.id = e.departmentId
    ) t
    where ranking <= 3 and salary is not null
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    窗口函数虽然好用,但是MySQL的版本必须是8或8以上才能使用,所以我们还是有必要掌握使用不同函数进行操作

    分析:我们进行一个自连接,然后统计出所有排名在前三的员工信息

    select d.name Department, e1.name Employee, e1.salary
    from Employee e1 join Department d on e1.DepartmentId = d.id 
    where 3 > (
        select count(distinct e2.salary)
        from Employee e2
        where e2.salary > e1.salary and e1.DepartmentId = e2.DepartmentId
    );
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    下面这个SQL是含义是查询出比当前员工(e1)薪资高的员工数量,因为比排名第一员工薪资高的员工数量为0,所以计数是从0开始,并不像先前窗口函数中排名是从1开始的,所以这里 3 是大于,不能取等号

        select count(distinct e2.salary)
        from Employee e2
        where e2.salary > e1.salary and e1.DepartmentId = e2.DepartmentId
    
    • 1
    • 2
    • 3

    PS:说实话这个SQL是我看官方题解是摘自官方题解,一开始看着是有点懵的,后面多看几遍才理解了,我个人任务使用窗口函数更易于理解,同时窗口函数提的SQL比这条SQL效率更高(窗口函数的SQL是90%左右,这条SQL是50%左右)

    修复表中的名字

    🔒题目

    题目来源:1667.修复表中的名字

    image-20231014170005644

    🔑题解

    • 考察知识点substringupperlowerconcat

      • substring(column_name, start, length):这将从列的值中提取一个子字符串,从指定的起始位置开始,直到指定的长度。注意截取索引是从1开始的,不是0
      • upper(expression):这会将字符串表达式转换为大写。
      • lower(expression):这会将字符串表达式转换为小写。
      • concat(string1, string2, ...):这会将两个或多个字符串连接成一个字符串。

      PS:这里只是讲解这些函数大概的作用,关于这些函数的详细使用大家可以参考MySQL教程

    分析:这一题其实说简单也简单,说难也难,因为只要你有了解相关操作字符的 函数 可以秒杀这一题,如果你并不了解相关的 函数 可能就毫无头绪,所以大家在做题的时候,不要去死磕题目,没做出来可以适当的直接看题解

    select user_id, concat(upper(substring(name, 1, 1)), lower(substring(name, 2, length(name)-1))) name
    from Users
    order by user_id asc;
    
    • 1
    • 2
    • 3

    备注:substring(name, 2, length(name)-1)这个函数可以简写为substring(name, 2)

    患某种疾病的患者

    🔒题目

    题目来源:1572.患某种疾病的患者

    image-20231014171657619

    🔑题解

    • 考察知识点like

    分析:直接使用 like 就可以了,但是需要注意一些比较细节的地方,DIAB1不一定出现在第一个位置,可以是第二个单词的位置,但是不能使用%DIAB1%去过滤,需要使用% DIAB1%这个去过滤,因为它是单词首位

    select *
    from Patients
    where conditions like 'DIAB1%' or conditions like '% DIAB1%';
    
    • 1
    • 2
    • 3

    这里再推荐一种更加简洁的写法,直接使用正则表达式,没想到正则表达式无处不在,在MySQL中也可以使用正则表达式,不得不说正则表达太强大了

    select patient_id, patient_name, conditions
    from Patients
    where conditions REGEXP '\\bDIAB1.*';
    
    • 1
    • 2
    • 3
  • 相关阅读:
    debian中vim的使用
    Discuz IIS上传附件大于28M失败报错Upload Failed.修改maxAllowedContentLength(图文教程)
    开发常用技巧(持续更新)
    java八股文面试[数据库]——分库分表
    Django之视图
    Qt之sendEvent
    docker启动命令,docker重启命令,docker关闭命令
    不完全解构和剩余运算符
    SegNet学习笔记
    HTML5+CSS3+JS小实例:鼠标控制飞机的飞行方向
  • 原文地址:https://blog.csdn.net/qq_66345100/article/details/133827870