如何快速掌握MYSQL?附LeetCode上出现频率最高的50道数据库题目详解
Posted 报告,今天也有好好学习
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了如何快速掌握MYSQL?附LeetCode上出现频率最高的50道数据库题目详解相关的知识,希望对你有一定的参考价值。
如何才能快速掌握mysql?如何熟悉使用SQL以满足日常工作需求?
如果你目前啥也不会,只停留在知道SELECT用于查询的层面的话,又想要快速掌握MYSQL,那么刷题,并且过程中不会什么补什么,就是巩固和提升自己的SQL语言能力最快捷的方法。
LeetCode中有不少题是需要Plus会员才能查看并答题的,所以为避免以后会员过期无法再次查看以及加深自身对题目的理解,进行了MYSQL题目的一次大整理。
LeetCode上题目众多,而本篇筛选出了LetCode上出现频率最高的50道题目,并且按出现频率由高到低整理,每道题实现的具体方法和思路都贴在了代码注释,整理不易,希望大家能够做完这些题目或者看完这篇博客,并从中有所获。
PS:本篇博客主要是使用增删查改中的查询功能。
目录
- NO.1 简单 (175. 组合两个表)
- NO.2 简单 (176. 第二高的薪水)
- NO.3 中等 (184. 部门工资最高的员工)
- NO.4 中等 (177. 第N高的薪水)
- NO.5 中等 (180. 连续出现的数字)
- NO.6 困难 (185. 部门工资前三高的所有员工)
- NO.7 简单 (1777. 每家商店的产品价格)
- NO.8 中等 (178. 分数排名)
- NO.9 简单 (181. 超过经理收入的员工)
- NO.10 困难 (262. 行程和用户)
- NO.11 困难 (601. 体育馆的人流量)
- NO.12 简单 (196. 删除重复的电子邮箱)
- NO.13 简单 (1179. 重新格式化部门表)
- NO.14 简单 (182. 查找重复的电子邮箱)
- NO.15 简单 (197. 上升的温度)
- NO.16 中等 (626. 换座位)
- NO.17 简单 (620. 有趣的电影)
- NO.18 简单 (627. 变更性别)
- NO.19 简单 (183. 从不订购的客户)
- NO.20 简单 (511. 游戏玩法分析 I)
- NO.21 简单 (595. 大的国家)
- NO.22 简单 (1873. 计算特殊奖金)
- NO.23 困难 (1097. 游戏玩法分析 V)
- NO.24 困难 (569. 员工薪水中位数)
- NO.25 中等 (1841. League Statistics)
- NO.26 困难 (571. 给定数字的频率查询中位数)
- NO.27 困难 (618. 学生地理信息报告)
- NO.28 简单 (1083. 销售分析 II)
- NO.29 中等 (1205. 每月交易II)
- NO.30 中等 (1501. 可以放心投资的国家)
- NO.31 简单 (1821. 寻找今年具有正收入的客户)
- NO.32 中等 (1204. 最后一个能进入电梯的人)
- NO.33 简单 (1082. 销售分析 I )
- NO.34 中等 (1098. 小众书籍)
- NO.35 中等 (1270. 向公司CEO汇报工作的所有人)
- NO.36 简单 (597. 好友申请 I:总体通过率)
- NO.37 中等 (1193. 每月交易 I)
- NO.38 简单 (1280. 学生们参加各科测试的次数)
- NO.39 中等 (1715. 苹果和橘子的个数)
- NO.40 简单 (1809. 没有广告的剧集)
- NO.41 简单 (577. 员工奖金)
- NO.42 简单 (603. 连续空余座位)
- NO.43 中等 (1112. 每位学生的最高成绩)
- NO.44 中等 (1308. 不同性别每日分数总计)
- NO.45 中等 (570. 至少有5名直接下属的经理)
- NO.46 中等 (580. 统计各专业学生人数)
- NO.47 简单 (1407. 排名靠前的旅行者)
- NO.48 简单 (1795. 每个产品在不同商店的价格)
- NO.49 中等 (550. 游戏玩法分析 IV)
- NO.50 困难 (579. 查询员工的累计薪水)
- 结束语
NO.1 简单 (175. 组合两个表)
https://leetcode-cn.com/problems/combine-two-tables/
# 因为题目要求无论 person 是否有地址信息,所以不能用join,要用left join
select FirstName, LastName, City, State
from Person
left join Address on Person.PersonId=Address.PersonId
NO.2 简单 (176. 第二高的薪水)
https://leetcode-cn.com/problems/second-highest-salary/
# 将第一高的工资筛选掉,再次用MAX就可以取到第二高的工资了。
select max(Salary) as SecondHighestSalary from Employee
where Salary <>
(select max(Salary) from Employee)
NO.3 中等 (184. 部门工资最高的员工)
https://leetcode-cn.com/problems/department-highest-salary/
# 先用group by的方法求出部门最高薪资,再接着判断Employee表中哪些数据符合即可,
# 过程中会用到join来连接Employee表和Department表。
select d.Name Department, e.Name Employee, Salary
from Employee e
join Department d on e.DepartmentId=d.Id
where (DepartmentId, Salary) in
(
select DepartmentId, MAX(Salary)
FROM Employee
group by DepartmentId
)
NO.4 中等 (177. 第N高的薪水)
https://leetcode-cn.com/problems/nth-highest-salary/
CREATE FUNCTION getNthHighestSalary(N INT) RETURNS INT
BEGIN
declare m int;
set m = N - 1;
RETURN (
# 这里主要是考LIMIT的用法:
/*
LIMIT 5 等价于 LIMIT 0, 5, 即查询(0+1)到(0+5)的数据,也就是1-5的数据。也就是说这里的0是查询的数据起点的前一位,5是查询的数量。
LIMIT 2 OFFSET 3 等价于 LIMIT 3 2,即查询(3+1)到(3+2)的数据,也就是4-5的数据。也就是说这里的3是查询的数据起点的前一位,2是查询的数量。
*/
# 那么要查排名第N的数据,那么就是LIMIT N-1, 1,所以是LIMIT m, 1
# 最后记得去重
SELECT IFNULL(
(SELECT DISTINCT Salary
FROM Employee
ORDER BY Salary DESC
LIMIT m, 1)
, NULL)
);
END
NO.5 中等 (180. 连续出现的数字)
https://leetcode-cn.com/problems/consecutive-numbers/
# 题目要求连续三次及以上,我们只要求出连续三次的数字有哪些即可。
# 取三个Logs表,用来判断。
SELECT DISTINCT L1.Num AS 'ConsecutiveNums'
FROM Logs l1, Logs l2, Logs l3
WHERE l1.Id-l2.Id=1 AND l2.Id-l3.Id=1 AND l1.Num=l2.Num AND l2.Num=l3.Num
NO.6 困难 (185. 部门工资前三高的所有员工)
https://leetcode-cn.com/problems/department-top-three-salaries/
/*
# 第一种方法是利用窗口函数和dense_rank(),能够对各个部门的工资进行排序
SELECT Department,Employee,Salary
FROM
(
SELECT
d.Name AS Department,
e.Name AS Employee,
e.Salary AS Salary,
DENSE_RANK() OVER (PARTITION BY e.DepartmentId ORDER BY e.Salary DESC) AS rk
FROM
Employee e
JOIN Department d
ON e.DepartmentId = d.Id
) AS t
WHERE rk<=3
*/
# 第二种方法运用到的思想是:部门中如果不超过两个工资比我的工资高,那我肯定是前三名。
# 比方说如果我是第二名,那只有一个工资比我高(如我工资是8000,部门其他人是9000,7000,9000,6000)
SELECT
d.Name AS Department,
e.Name AS Employee,
e.Salary AS Salary
FROM
Employee e
JOIN Department d
ON e.DepartmentId = d.Id
WHERE
e.Id IN
(
SELECT
e1.Id
FROM
Employee e1
LEFT JOIN Employee e2
on e1.DepartmentId = e2.DepartmentId AND e1.Salary < e2.Salary
GROUP BY e1.Id
HAVING COUNT(DISTINCT e2.Salary) <= 2
)
AND e.DepartmentId in (SELECT Id from Department)
order by d.Id asc,e.Salary desc
NO.7 简单 (1777. 每家商店的产品价格)
https://leetcode-cn.com/problems/products-price-for-each-store/
# 同题1795. 每个产品在不同商店的价格,正好相反
# 这里主要是记得要用到聚合函数
# 除了用IF函数,同样的也可以用CASE
SELECT
product_id,
SUM(IF(store='store1', price, NULL)) store1,
SUM(IF(store='store2', price, NULL)) store2,
SUM(IF(store='store3', price, NULL)) store3
FROM
Products
GROUP BY product_id
NO.8 中等 (178. 分数排名)
https://leetcode-cn.com/problems/rank-scores/
# 第一种方法:利用DENSE_RANK() 窗口函数
/*
SELECT
Score,
DENSE_RANK() OVER (ORDER BY Score DESC) AS 'Rank'
FROM
Scores
*/
# 第二种方法:主要思想是有几个分数(去重)大于等于我的分数,那这分数就是排第几。
SELECT
a.Score,
(
SELECT
COUNT(DISTINCT b.Score)
FROM
Scores b
WHERE a.Score <= b.Score
) AS 'Rank'
FROM
Scores a
ORDER BY a.Score DESC
NO.9 简单 (181. 超过经理收入的员工)
https://leetcode-cn.com/problems/employees-earning-more-than-their-managers/
# 自连接,将经理和对应的员工处于同一行,再比较即可。
SELECT e1.Name AS 'Employee'
FROM Employee e1 JOIN Employee e2 ON e1.ManagerId = e2.Id
WHERE e1.Salary > e2.Salary
NO.10 困难 (262. 行程和用户)
https://leetcode-cn.com/problems/trips-and-users/
# 解题思路:首先要将乘客或者司机是非禁止用户的行程筛选出来;
# 接着按日期分组,统计各个日期总订单数;
# 再判断按Status来统计其中取消的订单数量。
SELECT t.Request_at AS 'Day',
ROUND((SUM(IF(t.Status='completed', 0, 1))) /
COUNT(t.Status), 2) AS `Cancellation Rate`
/*
ROUND((SUM(IF(t.Status='cancelled_by_driver' or t.Status='cancelled_by_client', 1, 0))) /
COUNT(t.Status), 2) AS `Cancellation Rate`
*/
FROM Trips t
JOIN Users u1 ON (t.Client_Id=u1.Users_Id AND u1.Banned='No')
JOIN Users u2 ON (t.Driver_Id=u2.Users_Id AND u2.Banned='No')
WHERE t.Request_at BETWEEN '2013-10-01' AND '2013-10-03'
GROUP BY t.Request_at
NO.11 困难 (601. 体育馆的人流量)
https://leetcode-cn.com/problems/human-traffic-of-stadium/
# 窗口函数yyds
# 首先是将人流量大于100的数据筛选出来,然后用窗口函数根据id进行排序(一定要是rank而不是dense_rank)
# 利用id-rank,这样就可以将连续的数据分成同一个组(都拥有同一个order1)
# 接着再利用窗口函数根据order1分组并统计各组数量,大于等于3的数据即为所需数据。
SELECT id, visit_date, people
FROM
(
SELECT id, visit_date, people, COUNT(*) OVER(PARTITION BY order1) AS order2
FROM
(
SELECT *, (id - rank() OVER(ORDER BY id)) AS order1 FROM
(
SELECT * FROM Stadium WHERE people >= 100
) AS t1
) AS t2
) AS t3
WHERE order2 >= 3
NO.12 简单 (196. 删除重复的电子邮箱)
https://leetcode-cn.com/problems/delete-duplicate-emails/
# 注意是要用到DELETE操作。
DELETE p1
FROM Person p1, Person p2
WHERE p1.Email=p2.Email AND p1.Id>p2.Id
NO.13 简单 (1179. 重新格式化部门表)
https://leetcode-cn.com/problems/reformat-department-table/
# IF和CASE都可以,同时注意用上SUM函数。
/*
select id,
SUM(if(month="Jan", revenue, NULL)) as "Jan_Revenue",
SUM(if(month="Feb", revenue, NULL)) as "Feb_Revenue",
SUM(if(month="Mar", revenue, NULL)) as "Mar_Revenue",
SUM(if(month="Apr", revenue, NULL)) as "Apr_Revenue",
SUM(if(month="May", revenue, NULL)) as "May_Revenue",
SUM(if(month="Jun", revenue, NULL)) as "Jun_Revenue",
SUM(if(month="Jul", revenue, NULL)) as "Jul_Revenue",
SUM(if(month="Aug", revenue, NULL)) as "Aug_Revenue",
SUM(if(month="Sep", revenue, NULL)) as "Sep_Revenue",
SUM(if(month="Oct", revenue, NULL)) as "Oct_Revenue",
SUM(if(month="Nov", revenue, NULL)) as "Nov_Revenue",
SUM(if(month="Dec", revenue, NULL)) as "Dec_Revenue"
FROM Department
group by id
*/
select id,
SUM(CASE WHEN month="Jan" THEN revenue ELSE NULL END) as "Jan_Revenue",
SUM(CASE WHEN month="Feb" THEN revenue ELSE NULL END) as "Feb_Revenue",
SUM(CASE WHEN month="Mar" THEN revenue ELSE NULL END) as "Mar_Revenue",
SUM(CASE WHEN month="Apr" THEN revenue ELSE NULL END) as "Apr_Revenue",
SUM(CASE WHEN month="May" THEN revenue ELSE NULL END) as "May_Revenue",
SUM(CASE WHEN month="Jun" THEN revenue ELSE NULL END) as "Jun_Revenue",
SUM(CASE WHEN month="Jul" THEN revenue ELSE NULL END) as "Jul_Revenue",
SUM(CASE WHEN month="Aug" THEN revenue ELSE NULL END) as "Aug_Revenue",
SUM(CASE WHEN month="Sep" THEN revenue ELSE NULL END) as "Sep_Revenue",
SUM(CASE WHEN month="Oct" THEN revenue ELSE NULL END) as "Oct_Revenue",
SUM(CASE WHEN month="Nov" THEN revenue ELSE NULL END) as "Nov_Revenue",
SUM(CASE WHEN month="Dec" THEN revenue ELSE NULL END) as "Dec_Revenue"
FROM Department
group by id
NO.14 简单 (182. 查找重复的电子邮箱)
https://leetcode-cn.com/problems/duplicate-emails/
# 相同邮箱但不同Id即为重复数据(记得DISTINCT去重)
SELECT DISTINCT p1.Email
FROM Person p1, Person p2
WHERE p1.Email=p2.Email AND p1.Id<>p2.Id
NO.15 简单 (197. 上升的温度)
https://leetcode-cn.com/problems/rising-temperature/
# 两表之间相互比较即可
SELECT w1.id
FROM Weather w1, Weather w2
WHERE DATEDIFF(w1.recordDate,w2.recordDate)=1 AND w1.Temperature > w2.Temperature
NO.16 中等 (626. 换座位)
https://leetcode-cn.com/problems/exchange-seats/
# 关键在于如何将id转换,这里我用了^(异或)的方法
SELECT IF((SELECT COUNT(*) FROM seat)=id AND id%2<>0, id, (id+1)^1-1) AS id, student
FROM seat
ORDER BY id
NO.17 简单 (620. 有趣的电影)
https://leetcode-cn.com/problems/not-boring-movies/
# 没啥,就是WHERE的条件还有用到排序
SELECT *
FROM cinema
WHERE id%2=1 AND description<>'boring'
ORDER BY rating DESC
NO.18 简单 (627. 变更性别)
https://leetcode-cn.com/problems/swap-salary/
# 只能用UPDATE,然后结合CASE或者IF都可以。
UPDATE salary
SET
sex = CASE sex
WHEN 'm' THEN 'f'
ELSE 'm'
END;
NO.19 简单 (183. 从不订购的客户)
https://leetcode-cn.com/problems/customers-who-never-order/
# 判断哪些用户ID不在订单表里就行了
SELECT Name Customers FROM Customers
WHERE Id NOT IN (SELECT CustomerId FROM Orders)
NO.20 简单 (511. 游戏玩法分析 I)
https://leetcode-cn.com/problems/game-play-analysis-i/
# 按玩家ID分组,再取MIN(event_date)即可。
SELECT player_id, MIN(event_date) first_login
FROM Activity
GROUP BY player_id
NO.21 简单 (595. 大的国家)
https://leetcode-cn.com/problems/big-countries/
# 简单
SELECT name, population, area
FROM World
WHERE area > 3000000 or population > 25000000
NO.22 简单 (1873. 计算特殊奖金)
https://leetcode-cn.com/problems/calculate-special-bonus/
# 简单
SELECT employee_id, IF(employee_id%2=1 AND LEFT(name, 1)<>'M', salary, 0) bonus
FROM Employees
NO.23 困难 (1097. 游戏玩法分析 V)
https://leetcode-cn.com/problems/game-play-analysis-v/
# 先取出所有安装日期,然后求出每个安装日期有几个玩家;
# 其中第二天接着玩的有谁(判断玩的日期和安装日期是否相差一天),就可以了。
SELECT first_day AS install_dt,
COUNT(DISTINCT player_id) AS installs,
ROUND((SUM(IF(DATEDIFF(event_date, first_day)=1, 1, 0)))/COUNT(DISTINCT player_id), 2) AS Day1_retention
FROM
(
SELECT player_id, event_date, MIN(event_date) OVER(PARTITION BY player_id) AS first_day
FROM Activity
) AS t1
GROUP BY first_day
NO.24 困难 (569. 员工薪水中位数)
https://leetcode-cn.com/problems/median-employee-salary/
# 题目什么东西??不用任何内置函数,抱歉,窗口函数yyds
# 首先要按公司分组,然后对每个公司的员工工资排序(用ROW_NUMBER而不是RANK),然后通过中位数必定大于一半数同时小于一半数+1,得到了中位数。
SELECT Id, Company, Salary
FROM
(
SELECT *,
ROW_NUMBER() OVER(PARTITION BY Company ORDER BY Salary) AS rk,
COUNT(*) OVER(PARTITION BY Company) AS counts_cmy
FROM Employee
) AS t
WHERE rK>=counts_cmy/2 AND rK<=counts_cmy/2+1
NO.25 中等 (1841. League Statistics)
https://leetcode-cn.com/problems/league-statistics/
# 关键点在于连接的时候可以用or,其余就是相加罢了,不难。
SELECT
t.team_name,
COUNT(*) AS 'matches_played',
SUM(
CASE
WHEN (t.team_id=m.home_team_id AND m.home_team_goals>m.away_team_goals) OR
(t.team_id=m.away_team_id AND m.home_team_goals<m.away_team_goals) 如何快速掌握MYSQL?附牛客网精选的50道SQL题目详解新手推荐