文章目录
- 【LeetCode高频SQL50题-基础版】打卡第8天:第41~45题
- ⛅前言
- 好友申请II
- 🔒题目
- 🔑题解
- 2016年的投资
- 🔒题目
- 🔑题解
- 部门工资前三高的所有员工
- 🔒题目
- 🔑题解
- 修复表中的名字
- 🔒题目
- 🔑题解
- 患某种疾病的患者
- 🔒题目
- 🔑题解
【LeetCode高频SQL50题-基础版】打卡第8天:第41~45题
⛅前言
在这个博客专栏中,我将为大家提供关于 LeetCode 高频 SQL 题目的基础版解析。LeetCode 是一个非常受欢迎的编程练习平台,其中的 SQL 题目涵盖了各种常见的数据库操作和查询任务。对于计算机科班出身的同学来说,SQL 是一个基础而又重要的技能。不仅在面试过程中经常会遇到 SQL 相关的考题,而且在日常的开发工作中,掌握 SQL 的能力也是必备的。
本专栏的目的是帮助读者掌握 LeetCode 上的高频 SQL 题目,并提供对每个题目的解析和解决方案。我们将重点关注那些经常出现在面试中的题目,并提供一个基础版的解法,让读者更好地理解问题的本质和解题思路。无论你是准备找工作还是提升自己的技能,在这个专栏中,你可以学习到很多关于 SQL 的实践经验和技巧,从而更加深入地理解数据库的操作和优化。
我希望通过这个专栏的分享,能够帮助读者在 SQL 的领域里取得更好的成绩和进步。如果你对这个话题感兴趣,那么就跟随我一起,开始我们的 LeetCode 高频 SQL 之旅吧!
- 博客主页💖:知识汲取者的博客
- LeetCode高频SQL100题专栏🚀:LeetCode高频SQL100题_知识汲取者的博客-CSDN博客
- Gitee地址📁:知识汲取者 (aghp) - Gitee.com
- 题目来源📢:高频 SQL 50 题(基础版) - 学习计划 - 力扣(LeetCode)全球极客挚爱的技术成长平台
好友申请II
🔒题目
题目来源:602.好友申请II:谁有最多的好友
🔑题解
- 考察知识点:
子查询
、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
| id | num |
| -- | --- |
| 1 | 2 |
| 2 | 1 |
| 3 | 1 |
| 2 | 1 |
| 3 | 2 |
| 4 | 1 |
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;
2016年的投资
🔒题目
题目来源:585.2016年的投资
🔑题解
- 考察知识点:
round
、sum
、子查询
、group by
、in
、having
、count
分析:这一题题意还算十分清晰,我们只需要按照题目要求一步一步来即可,题目要求我们查询出满足下面两个条件的所有投保人的在2016年的总金额
- 在 2015 年的投保额 (
tiv_2015
) 至少跟一个其他投保人在 2015 年的投保额相同。 - 所在的城市必须与其他投保人都不同(也就是说 (
lat, lon
) 不能跟其他任何一个投保人完全相同)
我的思路是,先查询出满足第一个条件的所有 tiv_2015,然后插叙出所有满足第二个条件的 lat 和 lon,再将这两个条件进行取一个交集,最周在利用 sum + round 函数对筛选出的记录进行一个求和,最后就可以得到最终的答案了
1)查询出满足第一个条件的所有的 tiv_2015
select tiv_2015
from Insurance
group by tiv_2015
having count(*) > 1
| tiv_2015 |
| -------- |
| 10 |
2)查询出满足第二个条件的所有的 lat 和 lon
这里可以巧妙的利用 group by进行两个字段的分组,可能平常我们都是使用一个字段进行分组,实际上 group by可以同时按照多个字段进行分组
select lat, lon
from Insurance
group by lat, lon
having count(*) = 1
| lat | lon |
| --- | --- |
| 10 | 10 |
| 40 | 40 |
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
);
部门工资前三高的所有员工
🔒题目
题目来源:185.部门工资前三高的所有员工
🔑题解
- 考察知识点:
dense_rank
、子查询
、自连接
、count
、distinct
分析:这一题最开始我的第一反应就是使用窗口函数,因为这种分组排序使用窗口函数将十分的方便,具体的思路如下
- 确定使用哪一个窗口函数,这里可以明确使用 dense_rank 窗口函数(并列排序,不会跳过重复的序号 1、1、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
| 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 |
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
窗口函数虽然好用,但是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
);
下面这个SQL是含义是查询出比当前员工(e1)薪资高的员工数量,因为比排名第一员工薪资高的员工数量为0,所以计数是从0开始,并不像先前窗口函数中排名是从1开始的,所以这里 3 是大于,不能取等号
select count(distinct e2.salary)
from Employee e2
where e2.salary > e1.salary and e1.DepartmentId = e2.DepartmentId
PS:说实话这个SQL是我看官方题解是摘自官方题解,一开始看着是有点懵的,后面多看几遍才理解了,我个人任务使用窗口函数更易于理解,同时窗口函数提的SQL比这条SQL效率更高(窗口函数的SQL是90%左右,这条SQL是50%左右)
修复表中的名字
🔒题目
题目来源:1667.修复表中的名字
🔑题解
-
考察知识点:
substring
、upper
、lower
、concat
substring(column_name, start, length)
:这将从列的值中提取一个子字符串,从指定的起始位置开始,直到指定的长度。注意截取索引是从1开始的,不是0upper(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;
备注:substring(name, 2, length(name)-1)
这个函数可以简写为substring(name, 2)
患某种疾病的患者
🔒题目
题目来源:1572.患某种疾病的患者
🔑题解
- 考察知识点:
like
分析:直接使用 like 就可以了,但是需要注意一些比较细节的地方,DIAB1不一定出现在第一个位置,可以是第二个单词的位置,但是不能使用%DIAB1%
去过滤,需要使用% DIAB1%
这个去过滤,因为它是单词首位
select *
from Patients
where conditions like 'DIAB1%' or conditions like '% DIAB1%';
这里再推荐一种更加简洁的写法,直接使用正则表达式,没想到正则表达式无处不在,在MySQL中也可以使用正则表达式,不得不说正则表达太强大了
select patient_id, patient_name, conditions
from Patients
where conditions REGEXP '\\bDIAB1.*';