注册完leetcode 进行SQL刷题
使用的SQL语言为mysql,环境为mysql 5.7。
- Second Highest Salary
https://leetcode.com/problems/second-highest-salary/
Select
ifnull(
(select
Distinct Salary
from
Employee
order by
Salary desc
limit 1 offset 1)
,null) as SecondHighestSalary
-
limit … offset …的用法
针对已经排序的数据,可以选择任意排名的数据 -
ifnull()函数
-
需要考虑NULL的问题
讨论区另外一个可行的解法:
select
max(salary) as SecondHighestSalary
from
Employee
where
salary <> (select
max(salary)
from
Employee)
这可以有效避免null的问题。
从计算复杂度的角度来看,这两个答案应该没有区别。
- Rank Scores
https://leetcode.com/problems/rank-scores/
select
Score,
(select
count(*)
from
(select
distinct score s
from
scores) tmp
where
s>=score) Rank
from
Scores
order by
Score desc
答案来自于讨论区
dense_rank()
函数 也可以使用 但是 mysql server 5.7 不支持
- 需要理解SQL的运算过程
- Consecutive Numbers
https://leetcode.com/problems/consecutive-numbers/
# 针对每个Num 进行ID的分组排序 分组排序的代码类似于178
# 对于每一个数字找到其每一个排名 x :x+2 的ID
# 如果ID的差值为2 那么就是要找的数字
# 分组排序
select
distinct num as ConsecutiveNums
from
(select
t1.num
,t1.ID - t2.ID as diff
from
(select
id
,num
,(select
count(*)
from
(select
id as tmp_id,
num as tmp_num
from
logs ) tmp
where
tmp_id >= id
and tmp_num = num) as rank
from
logs) t1
-- 这是第一张表
left join
(select
id
,num
,(select
count(*)
from
(select
id as tmp_id
,num as tmp_num
from
logs ) tmp
where
tmp_id >= id and tmp_num = num) as rank
from
logs) t2
-- 这是第二张表 两张表一样
on
t1.num = t2.num
and t1.rank = t2.rank-2) tmp2
where
diff = 2
看过答案之后就会觉得自己写的真是不好。
和答案相比,这个的答案可以n次连续的问题 只需要把代码中的最后出现的两个常数2 替换成 n
SELECT *
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
;
直接内连三个表格,这样出现在最后结果表格中就是我们所要找的答案。这里建议用 inner join 速度会比较快。实际好像没有什么区别
- Rising Temperature
https://leetcode.com/problems/rising-temperature/
select
w1.id
from
weather w1
inner join
weather w2
on
w1.RecordDate - w2.RecordDate = 1 and w1.Temperature > w2.Temperature
不是很明白这个答案为什么会出错。
select
w1.id
from
weather w1
inner join
weather w2
on
datediff(w1.RecordDate, w2.RecordDate) = 1
and w1.Temperature > w2.Temperature
运算不一样:
select '2019-01-31' - '2019-02-1'
计算的值为0 而不是1。
- Employees Earning More Than Their Managers
https://leetcode.com/problems/employees-earning-more-than-their-managers/
select
e1.name as Employee
from
employee e1
left join
employee e2
on
e1.managerId = e2.id
and e1.managerId is not null
where
e1.salary > e2.salary
上面是正确答案。而下面的结果不正确:
select
e1.name as Employee
from
employee e1
left join
employee e2
on
e1.managerId = e2.id
and e1.managerId is not null
and e1.salary > e2.salary
为什么会出现这种情况?
参考资料
因为加 left join 会保留左边表格 在合并结果中 左表所有字段都保留。
- Department Highest Salary
https://leetcode.com/problems/department-highest-salary/
select
d.name as Department
,a.name as Employee
,max_sa as Salary
from
Department d
inner join
(select
tmp.DepartmentID
,tmp.max_sa
,employee.Name
from
(select
DepartmentID
,max(salary) as max_sa
from
employee
group by 1) tmp
left join
employee
on
employee.DepartmentId = tmp.DepartmentID
and employee.salary = tmp.max_sa) a
on a.DepartmentId = d.Id
-
用了子查询相对比较复杂
-
答案中的查询条件比较简单清晰
SELECT
Department.name AS 'Department',
Employee.name AS 'Employee',
Salary
FROM
Employee
JOIN
Department ON Employee.DepartmentId = Department.Id
WHERE
(Employee.DepartmentId , Salary) IN
( SELECT
DepartmentId, MAX(Salary)
FROM
Employee
GROUP BY DepartmentId
)
;
- Trips and Users
https://leetcode.com/problems/trips-and-users/
select
trips.request_at as day
,round(sum(case
when
position('cancelled' in trips.status) > 0
then
1
else 0
end)
/count(*)
,2) as "Cancellation Rate"
from
trips
left join
users u1
on
u1.users_id = trips.driver_id
left join
users u2
on
u2.users_id = trips.client_id
where
u2.banned = "no"
and u1.banned = "no"
and trips.Request_at between "2013-10-01" and "2013-10-03"
group by
trips.request_at
order by
trips.request_at asc
计算速度比较慢
在表连接的时候,不太能理解 left join user u1 on u1.users_id = trips.client_id or u1.users_id = trips.client_id 出现的计算结果
会出现重复的结果,笛卡尔积会出现重复,主要是因为使用了不唯一的key。
复习一下left join的计算过程:以 A left join B on …为例
-
做 A和 B的笛卡尔积
-
对笛卡尔积表格中,所有进行条件筛选
-
筛选结果中 加入没有在筛选结果但是在A表中的记录 B表所用字段记为null
-
Department Top Three Salaries
https://leetcode.com/problems/department-top-three-salaries/
select
Department.name as department
,tmp2.name as Employee
,tmp2.salary as salary
from
(select
*
,(select
count(*)
from
(select
salary as s
,departmentId as dID
from
employee) tmp
where
tmp.s >= salary
and departmentId = tmp.did) as rank
from
employee) tmp2
inner join
Department
on
tmp2.departmentId = Department.Id
where
rank < 3
这个只会出来三个人员。而不是薪水在前三的所用员工。
# Write your MySQL query statement below
# 按照Department 分组 排序
# 将排序号小于等于3 挑出来
# 分组排序怎么处理
#
# 紧凑排名 前三
select
Department.name as department
,tmp2.name as Employee
,tmp2.salary as salary
from
(select
*
,(select
count(*)
from
(select
salary as s
,departmentId as dID
from
employee
group by
1,2
having count(*) = 1) tmp
where
tmp.s > salary
and departmentId = tmp.did) as rank
from
employee) tmp2
inner join
Department
on
tmp2.departmentId = Department.Id
where
rank < 3
觉得应该是对的,但是不知道问题在哪?
- 分组distinct出错,去掉having条件,运行正确。
修改后的SQL:
select
Department.name as department
,tmp2.name as Employee
,tmp2.salary as salary
from
(select
*
,(select
count(*)
from
(select
salary as s
,departmentId as dID
from
employee
group by
1,2) tmp
where
tmp.s > salary
and departmentId = tmp.did) as rank
from
employee) tmp2
inner join
Department
on
tmp2.departmentId = Department.Id
where
rank < 3
答案的SQL更加简洁,在where语句中插入自查询。直接把rank变成一个自查询的结果。但是在运行时间上,答案运行时间更长。
SELECT
d.Name AS 'Department', e1.Name AS '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
)
;
- Human Traffic of Stadium
https://leetcode.com/problems/human-traffic-of-stadium/
# 排序 不能理解为什么没有结果
select
stadium.id
,(select
count(*)
from
(select
id
from
stadium
where
people > 100
) tmp
where
tmp.id >= id) as rank
from
stadium
where
people > 100
答案的解法非常犀利。
select
distinct s1.*
from
stadium s1
,stadium s2
,stadium s3
where
s1.people >= 100
and s2.people >= 100
and s3.people >= 100
and ((s1.id - s2.id = 1 and s3.id - s1.id = 1)
or (s2.id - s1.id = 1 and s3.id - s1.id = 2)
or (s1.id - s2.id = 1 and s1.id - s3.id = 2))
order by s1.id
- Exchange Seats
https://leetcode.com/problems/exchange-seats/
主要是利用数据库的左连接。
select
s1.id
,(case when s2.student is null then s1.student else s2.student end) as student
from
seat s1
left join
seat s2
on
(s1.id = s2.id - 1
and s1.id mod 2 = 1)
or
(s1.id = s2.id + 1
and s1.id mod 2 = 0)
order by
s1.id
在评论区看到的解法:
select
if(id < (select count(*) from seat), if(id mod 2=0, id-1, id+1), if(id mod 2=0, id-1, id)) as id, student
from seat
order by id asc;
四行代码解决问题,很高效。解题的思路和我的不一样,我的解题思路从互换name出发,而这个是直接交换id。