leetcode刷题 (数据库 MySQL) Part 2

本文是关于LeetCode中涉及MySQL的题目解析,包括上升的温度、行程和用户、大的国家等,讲解了如何使用SQL解决各种问题,涉及到日期差计算、表连接、窗口函数等知识点。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

LEETCODE数据库题目刷题(目前只有非会员的题目 😐),除了自己的题解还会整合大家的(10道题一个文档)
目前,非会员题目已经整理完 part 1 链接
LEETCODE数据库题库链接

197.上升的温度

题目:
给定一个 Weather 表,编写一个 SQL 查询,来查找与之前(昨天的)日期相比温度更高的所有日期的 Id。

Id(INT)RecordDate(DATE)Temperature(INT)
12015-01-0110
22015-01-0225
32015-01-0320
42015-01-0430

例如,根据上述给定的 Weather 表格,返回如下 Id:

Id
2
4

SQl架构:

Create table If Not Exists Weather (Id int, RecordDate date, Temperature int)
Truncate table Weather
insert into Weather (Id, RecordDate, Temperature) values ('1', '2015-01-01', '10')
insert into Weather (Id, RecordDate, Temperature) values ('2', '2015-01-02', '25')
insert into Weather (Id, RecordDate, Temperature) values ('3', '2015-01-03', '20')
insert into Weather (Id, RecordDate, Temperature) values ('4', '2015-01-04', '30')

leetcode链接

解答:
1.表连接 + datediff

select b.Id as Id
from Weather a
join Weather b
where datediff(b.RecordDate, a.RecordDate)=1 and a.Temperature < b.Temperature

这里直接用 a.RecordDate = b.RecordDate-1 的话,会出现不能跨年相减的问题,所以要老老实实用 datediff

2.表连接 + timestampdiff

select w2.Id
from Weather as w1, Weather as w2
where TIMESTAMPDIFF(DAY, w1.RecordDate, w2.RecordDate) = 1 AND w2.Temperature > w1.Temperature

3.变量

SELECT
	pc.id
from(
		select 
			if(@preT < a.Temperature AND DATEDIFF(a.RecordDate,@preD)=1,Id,null) AS id,
			@preT := a.Temperature,
			@preD := a.RecordDate
		from Weather a,
		(select @preT:=null,@preD:=null) AS b
		ORDER BY a.RecordDate
	) AS pc
WHERE pc.id is not null;

日期差

  1. datediff
    用法:datediff(date1, date2)
    计算两个日期相差的天数,前减后(不能作用于小时或分钟)。
    d1d2 可以是 DATEDATETIME 类型(可以不同)。如果是 DATE 类型,默认时间部分是“00:00:00”的 DATETIME 值。
select datediff('2015-04-22 06:00:00', '2015-04-28')   # -6
select datediff('2015-04-28 06:00:00', '2015-04-22')   # 6
  1. timestampdiff
    用法:timestampdiff(unit, d1, d2)
    计算两个时间相差的 年/月/日/小时/分钟/秒,后减前;
    d1d2 可以是 DATEDATETIME 类型(可以不同)。如果是 DATE 类型,默认时间部分是“00:00:00”的 DATETIME 值。
select timestampdiff(year, '2015-04-22', '2011-04-28')  # -3
select timestampdiff(year, '2011-04-28', '2015-04-22')  # 3
select timestampdiff(day, '2015-04-22 01:00:00', '2015-04-28 00:00:00')  # 5

262.行程和用户

题目:
Trips 表中存所有出租车的行程信息。每段行程有唯一键 Id,Client_Id 和 Driver_Id 是 Users 表中 Users_Id 的外键。Status 是枚举类型,枚举成员为 (‘completed’, ‘cancelled_by_driver’, ‘cancelled_by_client’)。

IdClient_IdDriver_IdCity_IdStatusRequest_at
11101completed2013-10-01
22111cancelled_by_driver2013-10-01
33126completed2013-10-01
44136cancelled_by_client2013-10-01
51101completed2013-10-02
62116completed2013-10-02
73126completed2013-10-02
821212completed2013-10-03
931012completed2013-10-03
1041312cancelled_by_driver2013-10-03

Users 表存所有用户。每个用户有唯一键 Users_Id。Banned 表示这个用户是否被禁止,Role 则是一个表示(‘client’, ‘driver’, ‘partner’)的枚举类型。

Users_IdBannedRole
1Noclient
2Yesclient
3Noclient
4Noclient
10Nodriver
11Nodriver
12Nodriver
13Nodriver

写一段 SQL 语句查出 2013年10月1日 至 2013年10月3日 期间非禁止用户的取消率。基于上表,你的 SQL 语句应返回如下结果,取消率(Cancellation Rate)保留两位小数。

取消率的计算方式如下:(被司机或乘客取消的非禁止用户生成的订单数量) / (非禁止用户生成的订单总数)

DayCancellation Rate
2013-10-010.33
2013-10-020.00
2013-10-030.50

注: 这里乘客和司机在一个表里,题目里的用户包括乘客和司机。

SQL架构:

Create table If Not Exists Trips (Id int, Client_Id int, Driver_Id int, City_Id int, Status ENUM('completed', 'cancelled_by_driver', 'cancelled_by_client'), Request_at varchar(50))
Create table If Not Exists Users (Users_Id int, Banned varchar(50), Role ENUM('client', 'driver', 'partner'))
Truncate table Trips
insert into Trips (Id, Client_Id, Driver_Id, City_Id, Status, Request_at) values ('1', '1', '10', '1', 'completed', '2013-10-01')
insert into Trips (Id, Client_Id, Driver_Id, City_Id, Status, Request_at) values ('2', '2', '11', '1', 'cancelled_by_driver', '2013-10-01')
insert into Trips (Id, Client_Id, Driver_Id, City_Id, Status, Request_at) values ('3', '3', '12', '6', 'completed', '2013-10-01')
insert into Trips (Id, Client_Id, Driver_Id, City_Id, Status, Request_at) values ('4', '4', '13', '6', 'cancelled_by_client', '2013-10-01')
insert into Trips (Id, Client_Id, Driver_Id, City_Id, Status, Request_at) values ('5', '1', '10', '1', 'completed', '2013-10-02')
insert into Trips (Id, Client_Id, Driver_Id, City_Id, Status, Request_at) values ('6', '2', '11', '6', 'completed', '2013-10-02')
insert into Trips (Id, Client_Id, Driver_Id, City_Id, Status, Request_at) values ('7', '3', '12', '6', 'completed', '2013-10-02')
insert into Trips (Id, Client_Id, Driver_Id, City_Id, Status, Request_at) values ('8', '2', '12', '12', 'completed', '2013-10-03')
insert into Trips (Id, Client_Id, Driver_Id, City_Id, Status, Request_at) values ('9', '3', '10', '12', 'completed', '2013-10-03')
insert into Trips (Id, Client_Id, Driver_Id, City_Id, Status, Request_at) values ('10', '4', '13', '12', 'cancelled_by_driver', '2013-10-03')
Truncate table Users
insert into Users (Users_Id, Banned, Role) values ('1', 'No', 'client')
insert into Users (Users_Id, Banned, Role) values ('2', 'Yes', 'client')
insert into Users (Users_Id, Banned, Role) values ('3', 'No', 'client')
insert into Users (Users_Id, Banned, Role) values ('4', 'No', 'client')
insert into Users (Users_Id, Banned, Role) values ('10', 'No', 'driver')
insert into Users (Users_Id, Banned, Role) values ('11', 'No', 'driver')
insert into Users (Users_Id, Banned, Role) values ('12', 'No', 'driver')
insert into Users (Users_Id, Banned, Role) values ('13', 'No', 'driver')

leetcode链接

解答:

1.表连接 + average

SELECT request_at as 'Day', round(avg(Status!='completed'), 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 request_at BETWEEN '2013-10-01' AND '2013-10-03'
GROUP BY request_at

average 可以计算多个数值为0和1的数的平均数,最后得到的结果就是1的比例。
这个方法也太棒了,我还在一直用 count 算来算去。。。

2.表连接 + if 判断 Status 是否为 ‘completed’ + SUM 求和 + COUNT 计数

SELECT
	T.request_at AS `Day`, 
	ROUND(SUM(IF(T.STATUS = 'completed',0,1))/COUNT(T.STATUS),2) AS `Cancellation Rate`
FROM Trips AS T
JOIN Users AS U1 ON (T.client_id = U1.users_id AND U1.banned ='No')
JOIN Users AS 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

事实证明,就算用 count 也可以很简单

3.这个是把第2种解法的 if 换成了case when,思路是一样的

select
	t.Request_at as Day,
	round (cast(sum(case when t.Status = 'completed' then 0 else 1 end) as float)/count(t.Status),2) as 'Cancellation Rate' 
from Trips t
	left join Users u on t.Client_ID = u.Users_Id
	left join Users u2 on t.Driver_ID = u2.Users_Id
where Request_at between '2013-10-01' and '2013-10-03' 
	and u.Banned = 'No'
	and u2.Banned = 'No'
group by t.Request_at

595.大的国家

题目:
这里有张 World 表:

namecontinentareapopulationgdp
AfghanistanAsia6522302550010020343000
AlbaniaEurope28748283174112960000
AlgeriaAfrica238174137100000188681000
AndorraEurope468781153712000
AngolaAfrica124670020609294100990000

如果一个国家的面积超过300万平方公里,或者人口超过2500万,那么这个国家就是大国家。

编写一个SQL查询,输出表中所有大国家的名称、人口和面积。

例如,根据上表,我们应该输出:

namepopulationarea
Afghanistan25500100652230
Algeria371000002381741

SQL架构:

Create table If Not Exists World (name varchar(255), continent varchar(255), area int, population int, gdp int)
Truncate table World
insert into World (name, continent, area, population, gdp) values ('Afghanistan', 'Asia', '652230', '25500100', '20343000000')
insert into World (name, continent, area, population, gdp) values ('Albania', 'Europe', '28748', '2831741', '12960000000')
insert into World (name, continent, area, population, gdp) values ('Algeria', 'Africa', '2381741', '37100000', '188681000000')
insert into World (name, continent, area, population, gdp) values ('Andorra', 'Europe', '468', '78115', '3712000000')
insert into World (name, continent, area, population, gdp) values ('Angola', 'Africa', '1246700', '20609294', '100990000000')

leetcode链接

1.where + or

select name, population, area
from World
where area > 3000000 or population > 25000000

2.where + union

select name, population, area
from World
where area > 3000000
union
select name, population, area
from World
where population > 25000000

union, union all, or

  1. 数据量较大或者对多列筛选时,用 union 会更比用 or 更快,or 用于多列筛选时,只有一列能用索引,其余列会全部扫描;
  2. union 会对结果去重,union all 不去重。

601.体育馆的人流量

题目:
X 市建了一个新的体育馆,每日人流量信息被记录在这三列信息中:序号 (id)、日期 (visit_date)、 人流量 (people)。

请编写一个查询语句,找出人流量的高峰期。高峰期时,至少连续三行记录中的人流量不少于100。

例如,表 stadium

idvisit_datepeople
12017-01-0110
22017-01-02109
32017-01-03150
42017-01-0499
52017-01-05145
62017-01-061455
72017-01-07199
82017-01-08188

对于上面的示例数据,输出为:

idvisit_datepeople
52017-01-05145
62017-01-061455
72017-01-07199
82017-01-08188

提示:

  • 每天只有一行记录,日期随着 id 的增加而增加。
  • 体育馆并不是每天都开放的,所以记录中的日期可能会出现断层。

SQL架构:

Create table If Not Exists stadium (id int, visit_date DATE NULL, people int)
Truncate table stadium
insert into stadium (id, visit_date, people) values ('1', '2017-01-01', '10')
insert into stadium (id, visit_date, people) values ('2', '2017-01-02', '109')
insert into stadium (id, visit_date, people) values ('3', '2017-01-03', '150')
insert into stadium (id, visit_date, people) values ('4', '2017-01-04', '99')
insert into stadium (id, visit_date, people) values ('5', '2017-01-05', '145')
insert into stadium (id, visit_date, people) values ('6', '2017-01-06', '1455')
insert into stadium (id, visit_date, people) values ('7', '2017-01-07', '199')
insert into stadium (id, visit_date, people) values ('8', '2017-01-08', '188')

leetcode链接

解答:
1.自连接 + union + order by

select s1.id, s1.visit_date, s1.people
from stadium s1
left join stadium s2
on s1.id = s2.id - 1
left join stadium s3
on s1.id = s3.id - 2
where s1.people >= 100 and s2.people >= 100 and s3.people >= 100
union
select s2.id, s2.visit_date, s2.people
from stadium s1
left join stadium s2
on s1.id = s2.id - 1
left join stadium s3
on s1.id = s3.id - 2
where s1.people >= 100 and s2.people >= 100 and s3.people >= 100
union
select s3.id, s3.visit_date, s3.people
from stadium s1
left join stadium s2
on s1.id = s2.id - 1
left join stadium s3
on s1.id = s3.id - 2
where s1.people >= 100 and s2.people >= 100 and s3.people >= 100
order by visit_date

卑微的我 union 了两次 😐 把 s1s2s3 的符合的 union 在一起,然后排序,要不然顺序错误不能通过。

2.官方题解,表连接 考虑 t1 是高峰的第一天、第二天和第三天三种情况,最后 distinct 一下

select distinct t1.*
from stadium t1, stadium t2, stadium t3
where t1.people >= 100 and t2.people >= 100 and t3.people >= 100
and
(
	(t1.id - t2.id = -1 and t1.id - t3.id = -2 and t2.id - t3.id = -1)  -- t1, t2, t3, t1是高峰第一天
    or
    (t2.id - t1.id = -1 and t2.id - t3.id = -2 and t1.id - t3.id = -1) -- t2, t1, t3, t1是高峰第二天
    or
    (t3.id - t2.id = -1 and t2.id - t1.id = -1 and t3.id - t1.id = -2) -- t3, t2, t1, t1是高峰第三天
)
order by t1.id

3.表连接 + 窗口函数 用 d 与排名相减,判断是不是连续id

with t1 as (
select
    id,
    visit_date,
    people,
#求出差值,因为id一定不会相同,所以使用最熟悉的rank就好
    id-rank() over(order by id) rk
from stadium
where people >= 100
)
select
    id,
    visit_date,
    people
from t1
#where条件过滤出条数大于3的
where rk in (
select rk from t1 group by rk having count(1) >= 3);

4.定义变量 cnt

select distinct s.*
from
	stadium s,
	(select id, visit_date, people, (@cnt:=IF(people>99, @cnt+1, 0)) as cnt
	 from stadium,(select @cnt:=0) b) c
where c.cnt > 2 and s.id between c.id-c.cnt+1 and c.id

626.换座位

题目:
小美是一所中学的信息科技老师,她有一张 seat 座位表,平时用来储存学生名字和与他们相对应的座位 id。

其中纵列的 id 是连续递增的

小美想改变相邻俩学生的座位。

你能不能帮她写一个 SQL query 来输出小美想要的结果呢?

示例:

idstudent
1Abbot
2Doris
3Emerson
4Green
5Jeames

假如数据输入的是上表,则输出结果如下:

idstudent
1Doris
2Abbot
3Green
4Emerson
5Jeames

注意: 如果学生人数是奇数,则不需要改变最后一个同学的座位。

SQL架构:

Create table If Not Exists seat(id int, student varchar(255))
Truncate table seat
insert into seat (id, student) values ('1', 'Abbot')
insert into seat (id, student) values ('2', 'Doris')
insert into seat (id, student) values ('3', 'Emerson')
insert into seat (id, student) values ('4', 'Green')
insert into seat (id, student) values ('5', 'Jeames')

leetcode链接

题解:
1.case when 进行id奇偶的人名调换,ifnull 实现奇数个学生时最后一个学生的名字不改变(交换学生)

select
    s.id,
    case
        when s.id % 2 = 1 then (ifnull((select student from seat where id = s.id+1), (select student from seat where id = s.id)))
        when s.id % 2 = 0 then (select student from seat where id = s.id-1)
    end as student
from seat s

2.case when (交换 id)官方题解

SELECT
    (CASE
        WHEN MOD(id, 2) != 0 AND counts != id THEN id + 1
        WHEN MOD(id, 2) != 0 AND counts = id THEN id
        ELSE id - 1
    END) AS id,
    student
FROM
    seat,
    (SELECT
        COUNT(*) AS counts
    FROM
        seat) AS seat_counts
ORDER BY id ASC;

3.(id + 1) ^ 1) - 1 位操作(异或) + 表连接 + COALESCE 官方题解

SELECT
    s1.id, COALESCE(s2.student, s1.student) AS student
FROM
    seat s1
        LEFT JOIN
    seat s2 ON ((s1.id + 1) ^ 1) - 1 = s2.id
ORDER BY s1.id;

异或是不同为1,相同为0,如:
在这里插入图片描述
所以有:0^1=11^1=02^1=33^1=2

SELECT id, (id+1)^1-1, student FROM seat

运行结果:

id(id+1)^1-1student
12Abbot
21Doris
34Emerson
43Green
56Jeames

表连接后,用 COALESCE() 使得奇数个id时最后一个学生的名字不改变

4.if + 判断最后一个是不是奇数(交换id)

select 
    if(id%2=0,
        id-1,
        if(id=(select count(distinct id) from seat),
            id,
            id+1)) 
    as id,student 
from seat 
order by id

知识点 COALESCE()

COALESCE() 函数可以接收多个参数,并返回第一个非 NULL 的参数。如果所有参数都为 NULL,则 COALESCE() 函数返回 NULL

知识点链接

627.交换工资

题目:
给定一个 salary 表,如下所示,有 m = 男性 和 f = 女性 的值。交换所有的 f 和 m 值(例如,将所有 f 值更改为 m,反之亦然)。要求只使用一个更新(Update)语句,并且没有中间的临时表。

注意,您必只能写一个 Update 语句,请不要编写任何 Select 语句。

例如:

idnamesexsalary
1Am2500
2Bf1500
3Cm5500
4Df500

运行你所编写的更新语句之后,将会得到以下表:

idnamesexsalary
1Af2500
2Bm1500
3Cf5500
4Dm500

SQL架构:

create table if not exists salary(id int, name varchar(100), sex char(1), salary int)
Truncate table salary
insert into salary (id, name, sex, salary) values ('1', 'A', 'm', '2500')
insert into salary (id, name, sex, salary) values ('2', 'B', 'f', '1500')
insert into salary (id, name, sex, salary) values ('3', 'C', 'm', '5500')
insert into salary (id, name, sex, salary) values ('4', 'D', 'f', '500')

leetcode链接

题解:
1.update + case when (官方题解)

UPDATE salary
SET
    sex = CASE sex
        WHEN 'm' THEN 'f'
        ELSE 'm'
    END

2.update + if

update salary
set sex = if(sex = 'm', 'f', 'm')

1179.重新格式化部门表

题目:
部门表 Department

Column NameType
idint
revenueint
monthvarchar

(id, month) 是表的联合主键。
这个表格有关于每个部门每月收入的信息。
月份(month)可以取下列值 [“Jan”,“Feb”,“Mar”,“Apr”,“May”,“Jun”,“Jul”,“Aug”,“Sep”,“Oct”,“Nov”,“Dec”]。

编写一个 SQL 查询来重新格式化表,使得新的表中有一个部门 id 列和一些对应 每个月 的收入(revenue)列。

查询结果格式如下面的示例所示:

Department 表:

idrevenuemonth
18000Jan
29000Jan
310000Feb
17000Feb
16000Mar

±-----±--------±------+

idJan_RevenueFeb_RevenueMar_RevenueDec_Revenue
1800070006000null
29000nullnullnull
3null10000nullnull

注意,结果表有 13 列 (1个部门 id 列 + 12个月份的收入列)。

SQL架构:

Create table If Not Exists Department (id int, revenue int, month varchar(5))
Truncate table Department
insert into Department (id, revenue, month) values ('1', '8000', 'Jan')
insert into Department (id, revenue, month) values ('2', '9000', 'Jan')
insert into Department (id, revenue, month) values ('3', '10000', 'Feb')
insert into Department (id, revenue, month) values ('1', '7000', 'Feb')
insert into Department (id, revenue, month) values ('1', '6000', 'Mar')

leetcode链接

题解:
1.case when + group by + sum / max / min

SELECT
	id,
	SUM(CASE `month` WHEN 'Jan' THEN revenue END) Jan_Revenue,
	SUM(CASE `month` WHEN 'Feb' THEN revenue END) Feb_Revenue,
	SUM(CASE `month` WHEN 'Mar' THEN revenue END) Mar_Revenue,
	SUM(CASE `month` WHEN 'Apr' THEN revenue END) Apr_Revenue,
	SUM(CASE `month` WHEN 'May' THEN revenue END) May_Revenue,
	SUM(CASE `month` WHEN 'Jun' THEN revenue END) Jun_Revenue,
	SUM(CASE `month` WHEN 'Jul' THEN revenue END) Jul_Revenue,
	SUM(CASE `month` WHEN 'Aug' THEN revenue END) Aug_Revenue,
	SUM(CASE `month` WHEN 'Sep' THEN revenue END) Sep_Revenue,
	SUM(CASE `month` WHEN 'Oct' THEN revenue END) Oct_Revenue,
	SUM(CASE `month` WHEN 'Nov' THEN revenue END) Nov_Revenue,
	SUM(CASE `month` WHEN 'Dec' THEN revenue END) Dec_Revenue
FROM Department
GROUP BY id

2.if + group by + sum / max / min

SELECT
	id,
	SUM(IF(`month`='Jan',revenue,NULL)) Jan_Revenue,
	SUM(IF(`month`='Feb',revenue,NULL)) Feb_Revenue,
	SUM(IF(`month`='Mar',revenue,NULL)) Mar_Revenue,
	SUM(IF(`month`='Apr',revenue,NULL)) Apr_Revenue,
	SUM(IF(`month`='May',revenue,NULL)) May_Revenue,
	SUM(IF(`month`='Jun',revenue,NULL)) Jun_Revenue,
	SUM(IF(`month`='Jul',revenue,NULL)) Jul_Revenue,
	SUM(IF(`month`='Aug',revenue,NULL)) Aug_Revenue,
	SUM(IF(`month`='Sep',revenue,NULL)) Sep_Revenue,
	SUM(IF(`month`='Oct',revenue,NULL)) Oct_Revenue,
	SUM(IF(`month`='Nov',revenue,NULL)) Nov_Revenue,
	SUM(IF(`month`='Dec',revenue,NULL)) Dec_Revenue
FROM Department
GROUP BY id

这个题重点在于 group by 之后,一个id对应多个月份,要用聚合函数取出想要的月份。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值