这段时间,刷了LeetCode的数据库题目,挺有感触的。在刷这些题之前,我只写过一般的增删改查,尤其是查询操作,使用的也不是很灵活,只知道最基本的用法。
去刷了题目之后真的学到了很多不论是新的知识点还是一些组合用法,总之对数据库操作尤其是dql的使用更加熟练了。感谢其他leetcoder对各种知识点和解题套路的分享,学会这些小操作后,写sql头不疼了,腰不酸了,睡得着了,吃饭也更香了。
根据这些题,我总结了一些常用的知识点、小技巧以及部分有代表性的题解。
这篇文章就来分享一些有价值的题解。题目有些多,这篇先展示其中八题的题解。
剩下的在这里
题目链接
真的很经典的一道题了,按照题号开始刷题的话这是第一个碰上的不好写的题目。
主要难点在于对同一个表中某些行的特定字段进行比较。由于SQL不能像一般编程语言那样自由的对表中的数据进行“遍历”
对于这样的题,特点就是比较不同行的同一列,而非同一行的不同列,不便于直接处理。
解决它的方法就是进行自联结,在该表的笛卡尔积中我们能找到所有行与行的组合,再使用条件筛选出其中需要比较的行,剩下的问题就转化成了同一行的不同列进行比较。
select
e1.name as employee
from
employee e1
left join employee e2 on e1.managerId = e2.id
where
e1.salary > e2.salary;
直接使用自联结,将那些需要比较的行的组合(相邻的两行)筛选下来,进行同一行不同字段的比较。
select
c1.seat_id
from
cinema c1
left join cinema c2 on c1.seat_id = c2.seat_id - 1
left join cinema c3 on c1.seat_id = c3.seat_id + 1
where
c1.free = 1
and
(
c2.free = 1
or
c3.free = 1
)
;
题目链接
同上一题相似,这一题也是对一张表中的不同行的同一列进行比较。
我们直接使用上面的套路,进行自联结,筛选掉不合适的组合(不是差一天的关系)
剩下的就是同一行不同列的比较了。
SELECT
a.id as id
FROM
weather a
join weather b
on datediff(a.recorddate,b.recorddate) = 1
and a.temperature > b.temperature
;
题目链接
一种不太常见的题型——使用DML对表单数据进行删除处理
这道理另一个不好处理的问题在于,如果使用子查询将所有重复的第一个id查询出来再使用where条件删除剩余不重复的元素会因为操作表单和查询表单相同而报错
一种可行的处理办法是套一层娃,将查询出来的表单再嵌套一层查询,用一个临时的表单代替待操作的表单。
delete from
person
where
id not in(
select
id
from(
select
min(id) as id
from
person
group by
email
)t
)
还有一种更简洁的方法,是使用多表删除的操作。多表删除是5.0版本以后新增的操作,他允许我们进行多表联结,并将进行删除操作表的出现在联结表单中的行删除。
delete
p1
from
person p1,
person p2
where
p1.email = p2.email
and
p1.id > p2.id
;
题目链接
这个题目需要我们首先处理玩家第一次登录的时间,之后再找出玩家在这个登录时间使用的设备
难点在于匹配登录设备时,主键是玩家的id和他第一次登录的时间,这个需要我们是用多个字段同时使用in来进行匹配:
select
player_id,
device_id
from
activity
where
(player_id,event_date) in(
select
player_id,
min(event_date)
from
activity
group by
player_id
);
题目链接
这个题目需要我们从两个表单中查询出一个结果值并进行运算,另外,还需要在各自的表单中进行去重。
有一种去重方式是进行分组,但是由于这道题需要使用到聚合函数对整个表单的结果进行统计,因此这道题不适合使用分组去重。
另外一种去重方式就是使用关键字distinct
,分别根据各自表中的两个关键字进行去重。
select
round(ifnull(
(select count(distinct requester_id,accepter_id) from request_accepted)/
(select count(distinct sender_id,send_to_id) from friend_request)
,0),2) as accept_rate;
题目需要我们找出所有没有向red公司出售过商品的销售员,正着做不好搞,我们就反着来。
先查找出所有项red公司出售过商品的销售员,再从所有的销售员中除去这些销售员即可。
SELECT
s.name as name
FROM
salesperson s
WHERE
s.sales_id NOT IN (
SELECT sales_id
FROM orders
WHERE
orders.com_id in (
select com_id
from company
where name = 'RED'
)
);
我们使用自联结,筛选出所有不同行的组合。
在这之后,计算差的绝对值就非常容易了,我们直接输出最小的那个就行。
输出最小的那个可以采用排序的方法,或者使用聚合函数min
SELECT
ABS(x1.x - x2.x) as shortest
FROM
point x1
JOIN point x2
on x1.x != x2.x
ORDER BY
ABS(x1.x - x2.x)
LIMIT 0,1;
SELECT
min(ABS(x1.x - x2.x)) as shortest
FROM
point x1
JOIN point x2
on x1.x != x2.x
;