🎈写在前面
🙋♂️大家好呀,我是超梦。小伙伴们都知道,不管是在学习中还是日常工作中,几乎天天是要跟数据库打交道的,为了更好的操作数据库,我们的SQL知识储备是必不可少的。想要掌握好SQL,那少不了每天的练习与学习。接下来小梦会带领小伙伴们开启LeetCode-MySQL强化训练,通过力扣真题来复习巩固我们的SQL知识,能在以后的工作与学习中熟练使用SQL语句。小梦会在每到题后面附加上对应的知识点,方便小伙伴们查缺补漏。
🙋♂️ 小伙伴们如果在学习过程中有不明白的地方,欢迎评论区留言提问,小梦定知无不言,言无不尽。
目录
题目一:组合两个表
题目概述
解题思路
代码测试
知识点小结
联表查询之外连接
题目二:第二高的薪水
题目概述
解题思路
代码测试
知识点小结
limit子句
ifnull函数
题目三:超过经理收入的员工
题目概述
解题思路
代码测试
知识点小结
内连接与外连接
题目四:查找重复的电子邮箱
题目概述
解题思路
代码测试
知识点小结
group by与having子句
题目五:从不订购的顾客
题目概述
解题思路
代码测试
知识点小结
内连接与外连接
题目一:组合两个表
题目概述
题目:
编写一个 SQL 查询,满足条件:无论 person 是否有地址信息,都需要基于表1和表2两表提供 person 的以下信息:
FirstName, LastName, City, State
LeetCode原题地址,点击进入~
表1:
Person
+-------------+---------+ | 列名 | 类型 | +-------------+---------+ | PersonId | int | | FirstName | varchar | | LastName | varchar | +-------------+---------+ PersonId 是Person表主键
表2:
Address
+-------------+---------+ | 列名 | 类型 | +-------------+---------+ | AddressId | int | | PersonId | int | | City | varchar | | State | varchar | +-------------+---------+ AddressId 是Address表主键 PersonId 是表 Person 的外键
解题思路
1. 我们先通读一下题目,先看题目中提到的表1(person)和表2(address),从表的结构可以看出,表1(person)是人的姓名信息,表2(address)是人的地址信息。
2. 我们都要通过Person表与AddressId表来查询出(FirstName, LastName, City, State)这四个字段的信息。查询结果是两个表里的列名,所以需要多表查询。
3. 可能不是每个人都有地址信息,所以查询出来的数据有的person就会有City,State两个字段的信息,有的person就会没有,对应显示为null。考虑到有的人可能没有地址信息,要是查询结构要查所有人,需要保留表1(Person)里的全部数据,所以用左联结(left join)。
4.
表 Address 中的 PersonId 是表 Person 的外关键字,两个表通过personId产生联结。
代码测试
这里我们使用的是左外连接
select FirstName, LastName, City, State from Person left join Address on Person.PersonId = Address.PersonId;
在LeetCode上执行测试是否成功
与预期结果一致,通过!
知识点小结
联表查询之外连接
该题目主要考察了联表查询外连接的相关知识点,接下来带着小伙伴们快速回顾一下外连接的相关知识点,帮助大家学习与巩固。
外连接分为三种:左外连接(left join),右外连接(right join),全外连接(full join)。这里我们省略了outer 这个关键字。
外连接的一个重要特点:至少有一方保留全集,没有匹配行用NULL代替。
下面小梦把这三种外连接简单说一下:
1. LEFT OUTER JOIN,简称LEFT JOIN,左外连接(左连接)
结果集保留左表的所有行,但右表只包含与左表匹配的行。右表相应的空行为NULL值。
SELECT * FROM 表1 LEFT JOIN 表2 ON 表1.xx = 表2.xx
2. RIGHT OUTER JOIN,简称RIGHT JOIN,右外连接 (右连接)
结果集保留右表的所有行,但左表只包含与右表匹配的行。左表相应的空行为NULL值。
SELECT * FROM 表1 RIGHT JOIN 表2 ON 表1.xx = 表2.xx
3. FULL OUTER JOIN,简称FULL JOIN,全外连接
会把两个表所有的行都显示在结果表中。
SELECT * FROM 表1 FULL JOIN 表2 ON 表1.xx = 表2.xx
题目二:第二高的薪水
题目概述
Employee
表Employee 表 +----+--------+ | Id | Salary | +----+--------+ | 1 | 100 | | 2 | 200 | | 3 | 300 | +----+--------+
mployee
表中第二高的薪水(Salary),如果不存在第二高的薪水,那么查询应返回null
。- 例如上述
Employee
表,SQL查询应该返回200
作为第二高的薪水。+---------------------+ | SecondHighestSalary | +---------------------+ | 200 | +---------------------+
👇LeetCode原题地址,点击进入~https://leetcode-cn.com/problems/combine-two-tables/
解题思路
方式一
- 首先我们先写一个select查询语句,查出Employee表中最高的薪水。
select max(salary) from Employee;
2. 然后我们把第一步的查询语句作为一个子句,我们通过where条件,使salary小于该子句得到的最高薪水。简单来讲就是我查询到一个小于最高的薪水的最高薪水,不就是第二高薪水嘛。
select max(salary) from Employee where salary < (select max(salary) from Employee);
3. 接下来我们根据题意还需要做一个null判断,如果不存在第二高薪水,就返回null值。这里我们用到了ifnull函数(文章知识点总结部分会有对ifnull函数的讲解哦~)。
题解:
select ifnull(( select max(salary) from Employee where salary < (select max(salary) from Employee)),null) as SecondHighestSalary;
方式二
- 首先我们将salary字段进行降序排序。
- 通过distinct将alary中的值去重。这里可能会有小伙伴们问了,employee表中salary没有重复的值呀为什么要加去重操作?小梦跟小伙伴们说一下,题目表中数据量很少,当数据量大的时候我们不能保证没有相同数据。当出现重复数据而我们没有进行去重操作,那我们通过降序排序也无法判断第二高的数据在哪一位置。当进行去重操作后,我们通过降序排序就能很清楚的知道第二高的数据就是排在第二位。所以为了谨慎与正确性,我们需要加上distinct。
- 通过以上去重排序操作后,我们很清楚的知道第二高的薪水就是排在第二位,再通过limit 1,1或者limit 1 offset1来查询出第二高的薪水。(limit1,1:前面的那一个1意思是跳过一条数据,后面的1是取一个数据,意思就是跳过一个数据,从第二条数据开始取一条数据。)(limit 1 offset 1:前面那个1意思是取一个数据,后面那一个1是跳过一条数据,整个意思就是跳过一个数据,从第二条数据开始取一条数据。)
- 接下来我们根据题意还需要做一个null判断,如果不存在第二高薪水,就返回null值。
题解:
##limit selectifnull((select distinct Salaryfrom Employeeorder by Salary desclimit 1,1), null) as SecondHighestSalary;## limit offset selectifnull((select distinct Salaryfrom Employeeorder by Salary desclimit 1 offset 1), null) as SecondHighestSalary;
代码测试
方式一
select ifnull((
select max(salary) from Employee
where
salary < (select max(salary) from Employee)),null)
as SecondHighestSalary;
输出与预期结果一致,答题成功!
方式二
limit
selectifnull((select distinct Salaryfrom Employeeorder by Salary desclimit 1,1), null) as SecondHighestSalary;
输出与预期结果一致,答题成功!
limit offset
SELECTIFNULL((SELECT DISTINCT SalaryFROM EmployeeORDER BY Salary DESCLIMIT 1 OFFSET 1),NULL) AS SecondHighestSalary
输出与预期结果一致,答题成功!
知识点小结
limit子句
我们来简单回顾一下limit与limit offset的知识点,要常常温故而知新~
- limit n 分句表示: 读取 n 条数据
- limit n, m 分句表示: 跳过 n 条数据,读取 m 条数据
- limit n 等价于 limit 0,n
- limit m offset n 分句表示: 跳过 n 条数据,读取 m 条数据
ifnull函数
通过今天这一题,相信小伙伴们对ifnull函数也有些认识了。
ifnull(expression ,y)函数解释:
如果第一个参数的表达式 expression 为 NULL,则返回第二个参数y的值(此题中是返回null值)。
如果第一个参数的表达式 expression 为 不为NULL,则返回第一个参数表达式expression的值。
题目三:超过经理收入的员工
题目概述
Employee
表+----+-------+--------+-----------+ | Id | Name | Salary | ManagerId | +----+-------+--------+-----------+ | 1 | Joe | 70000 | 3 | | 2 | Henry | 80000 | 4 | | 3 | Sam | 60000 | NULL | | 4 | Max | 90000 | NULL | +----+-------+--------+-----------+
题目:
Employee
表包含所有员工,他们的经理也属于员工。每个员工都有一个 Id,此外还有一列对应员工的经理的 Id。给定Employee
表,编写一个 SQL 查询,该查询可以获取收入超过他们经理的员工的姓名。在上面的表格中,Joe 是唯一一个收入超过他的经理的员工。查询出的结果如下所示。+----------+ | Employee | +----------+ | Joe | +----------+
👇LeetCode原题地址~https://leetcode-cn.com/problems/employees-earning-more-than-their-managers/
解题思路
首先根据题意,Employee表包含所有员工,他们的经理也属于员工。每个员工都有一个 Id,此外还有一列对应员工的经理的 Id。那我们看到字段ManagerId就是对应员工的经理Id,由Employee表得知joe对应的经理Id是3,Henry对应的经理Id是4。经理也属于员工,id3对应的是Sam,Id4对应的是Max,也就是说joe对应的经理是Sam,Henry对应的经理是Max。
因为表既有员工信息又有经理信息,所以我们需要获取两次信息,我们可以把Employee既看成员工表又看成经理表。题目要求我们查找出员工工资大于经理的,那我们可以通过id字段与ManagerId字段做连接,加一个员工Salary大于经理Salary条件即可。具体方法如下。
方法一
表中存在员工与经理两种信息,我们需要获取两次信息,然后使用Where语句做条件筛查,找出员工工资大于经理的数据。
题解1:
SELECT staff.Name AS 'Employee' FROMEmployee AS staff,Employee AS manager WHEREstaff.ManagerId = manager.Id AND staff.Salary > manager.Salary
方法二
表中存在员工与经理两种信息,我们需要获取两次信息,除了使用where语句外,我们还可以使用内连接,通过on语句做条件筛查来找出员工工资大于经理的数据。
题解2:
SELECT staff.NAME AS 'Employee' FROM Employee AS staff JOIN Employee AS manager ON staff .ManagerId = manager.Id AND staff .Salary > manager.Salary
代码测试
方法一
SELECT staff.Name AS 'Employee' FROMEmployee AS staff,Employee AS manager WHEREstaff.ManagerId = manager.Id AND staff.Salary > manager.Salary
与预测结果一致,成功!
方法二
SELECT staff.NAME AS 'Employee' FROM Employee AS staff JOIN Employee AS manager ON staff .ManagerId = manager.Id AND staff .Salary > manager.Salary
与预测结果一致,成功!
知识点小结
内连接与外连接
小梦用极简单的方式带小伙伴们过一遍内连接与外连接。
表1 classa
表2 classb
1. 内连接 inner join (join 默认就是内连接)
表1与表2的交集,用上面两个表演示一下
select classa.id as aid,classb.id as bid from classa inner join classb on classa.id = classb.id;
查询的结果是classa与classb的交集
2. 左外连接 left join
结果集保留左表的所有行,但右表只包含与左表匹配的行。右表相应的空行为NULL值。
select classa.id as aid,classb.id as bid from classa left join classb on classa.id = classb.id;
3. 右外连接 right join
结果集保留右表的所有行,但左表只包含与右表匹配的行。左表相应的空行为NULL值。
select classa.id as aid,classb.id as bid from classa right join classb on classa.id = classb.id;
4. 全外连接 full join
会把两个表所有的行都显示在结果表中。
select classa.id as aid,classb.id as bid from classa full join classb on classa.id = classb.id;
小伙伴们注意啦!!!
MySQL不支持full join!!!MySQL不支持full join!!!MySQL不支持full join!!!
重要的事情要说三遍!!!那怎么实现和full join一样的效果呢?那就要通过使用union来实现,具体实现SQL语句如下
select classa.id as aid,classb.id as bid from classa left join classb on classa.id = classb.id union select classa.id as aid,classb.id as bid from classa right join classb on classa.id = classb.id;
题目四:查找重复的电子邮箱
题目概述
题目:
编写一个 SQL 查询,查找
Person
表中所有重复的电子邮箱。Person表
+----+---------+ | Id | Email | +----+---------+ | 1 | a@b.com | | 2 | c@d.com | | 3 | a@b.com | +----+---------+
根据以上输入,你的查询应返回以下结果:
+---------+ | Email | +---------+ | a@b.com | +---------+
解题思路
其实这道题很简单啦,相信小伙伴们心中已经知道该怎么做了,赶紧去LeetCode挥笔写下答案测试一番吧!!测试完可以再看看小梦与大家的思路是否一致呢,如果有多种思路,欢迎留言写下你们的思路与解法,大家互相学习学习~
根据题意,要找出表中所有重复的电子邮箱,也就是Email字段对应的数据有重复的我需要查找出来。那我们可以使用count函数来获取Email中各电子邮箱的数量,只要数量大于1就是题目中要求的。接下来,根据该思路,小梦列出一下三种解法,小伙伴们如果有其它思路,欢迎补充~
方法1
我们可以先查出Email和对应Email的个数,把查出的内容当作一个临时表,在通过查该临时表,找出Email数量大于的1的,这样我们就查找出了题目所要求的【找出表中所有重复的电子邮箱】。
题解:
select Email from (select Email, count(Email) as num from Person group by Email) as temporary where num > 1;
方法2
我们可以通过group by对Email进行分组,然后通过having进行条件筛查,having后面可以运用聚合函数非常方便(知识点小结部分会有group by与having的简单讲解,小伙伴们记得查阅哦~)。通过聚合函数count(Email)在having后进行条件筛查,找出Email个数大于1的,这样我们就查找出了题目所要求的【找出表中所有重复的电子邮箱】。
题解:
select Email from Person group by Email having count(Email) > 1;
方法3
第三种方法大家可以发散一下思维,如果不用count函数怎么知道重复的Email呢?怎么查呢?小梦来带小伙伴们一起思考,首先我们可以想想,前两种方法实质上都是通过查出Email数判断是否大于1,大于1就是我们所要的答案,不大1就不是。那大家有没有注意表Person虽然Email有重复的,但是id是唯一的,那我们就可以把Person表看成两张表,一张a表和一张b表。连接两张表,where语句后面加上a表的Email等于b表的Email和a表的id不等于b表的id。然后查询Email做去重操作,就得到了我们想要的答案。
题解:
select distinct a.Email from Person a,Person b where a.Email = b.Email and a.id <> b.id;
代码测试
方法1
select Email from
(select Email, count(Email) as num from Person group by Email) as temporary
where num > 1;
输入题解,进行测试
测试成功!
方法2
select Email from Person group by Email having count(Email) > 1;
输入题解,进行测试
测试成功!
方法3
select distinct a.Email from Person a,Person b where a.Email = b.Email and a.id <> b.id;
输入题解,进行测试
测试成功!
知识点小结
group by与having子句
SQL中的数据可以按列名分组,可以搭配聚合函数一起使用,方便我们对于数据的查询与获取。
例:
SELECT count(stuname) FROM student GROUP BY student_class;
使用GROUP BY分组语句可以与WHERE语句一起使用,当然这里有个非常重要的地方希望小伙伴们记下来,非常重要!->当一条SQL中有聚合函数,WHERE语句,GRUOP BY语句时,他们的执行顺序是怎么的呢?WHERE >GROUP BY>聚合函数。
所以在WHERE语句执行过滤条件中我们不能使用聚合函数,使用的话就会报错,在这我们可以使用HAVING子句执行过滤条件筛查时使用聚合函数。小伙伴们一定要记住!
最后呢小梦再分享一下SQL查询语句各关键字的执行顺序,可以拿小本本记一下了,有用的很~
【重点】SQL的执行顺序:
- 先执行FROM
- 再执行WHERE条件过滤
- 再执行GROUP BY分组
- 再执行SELECT,聚集函数
- 再执行HAVING条件过滤
- 再执行ORDER BY 排序
题目五:从不订购的顾客
题目概述
Customers
表:+----+-------+ | Id | Name | +----+-------+ | 1 | Joe | | 2 | Henry | | 3 | Sam | | 4 | Max | +----+-------+
Orders
表:+----+------------+ | Id | CustomerId | +----+------------+ | 1 | 3 | | 2 | 1 | +----+------------+
某网站包含两个表,
Customers
表和Orders
表。编写一个 SQL 查询,找出所有从不订购任何东西的客户。例如给定上述表格,你的查询应返回:
+-----------+ | Customers | +-----------+ | Henry | | Max | +-----------+
LeetCode原题链接~ 点击进入https://leetcode-cn.com/problems/customers-who-never-order/
解题思路
根据题目,我们先看题目要求我们查什么,要查出从不订购任何东西的顾客。根据这一点我们可以从
Orders
订单表中得知CustomerId为1和3的是订购过东西的顾客,而2和4则没有订购过任何东西。再从Customers
顾客表中查id2与id4对应的顾客名字就查到了。
方法一
1. 根据解题思路,我们可以先写一个子查询查出Orders订单表中对应的CustomerId顾客Id
select customerid from orders;
2. 查出顾客Id后,我们就知道谁买过东西谁从来不买东西。我们再通过NOT IN子句给顾客Id做限制,查Customers表中顾客Id不在Orders表中的就是题目所要求的。
select customers.name as 'Customers' from customers where customers.id not in (select customerid from orders);
LeetCode原题解析https://leetcode-cn.com/problems/customers-who-never-order/solution/cong-bu-ding-gou-de-ke-hu-by-leetcode/
方法二
我们通过左外链接,把
Customers
表与Orders
表链接起来,我们只需要查链接后Orders表的数据为NULL的数据,就是从来没有买过东西的顾客。select c.name as Customers from Customers c left join Orders o on c.id = o.CustomerId where o.id is null;
代码测试
方法一
SQL代码
select customers.name as 'Customers' from customers where customers.id not in (select customerid from orders);
执行代码,测试
测试成功!
方法二
SQL代码
select c.name as Customers from Customers c left join Orders o on c.id = o.CustomerId where o.id is null;
执行代码,测试
测试成功
知识点小结
内连接与外连接
表1 classa
表2 classb
1. 内连接 inner join (join 默认就是内连接)
表1与表2的交集,用上面两个表演示一下
select classa.id as aid,classb.id as bid from classa inner join classb on classa.id = classb.id;
查询的结果是classa与classb的交集
2. 左外连接 left join
结果集保留左表的所有行,但右表只包含与左表匹配的行。右表相应的空行为NULL值。
select classa.id as aid,classb.id as bid from classa left join classb on classa.id = classb.id;
3. 右外连接 right join
结果集保留右表的所有行,但左表只包含与右表匹配的行。左表相应的空行为NULL值。
select classa.id as aid,classb.id as bid from classa right join classb on classa.id = classb.id;
4. 全外连接 full join
会把两个表所有的行都显示在结果表中。
select classa.id as aid,classb.id as bid from classa full join classb on classa.id = classb.id;
小伙伴们注意啦!!!
MySQL不支持full join!!!MySQL不支持full join!!!MySQL不支持full join!!!
重要的事情要说三遍!!!那怎么实现和full join一样的效果呢?那就要通过使用union来实现,具体实现SQL语句如下
select classa.id as aid,classb.id as bid from classa left join classb on classa.id = classb.id union select classa.id as aid,classb.id as bid from classa right join classb on classa.id = classb.id;
😀感谢小伙伴们支持,如果有什么疑问,欢迎留言询问,小梦定知无不言,言无不尽!