LEETCODE数据库题目刷题(目前只有非会员的题目 😐),除了自己的题解还会整合大家的(10道题一个文档)
目前,非会员题目已经整理完 part 1 链接
LEETCODE数据库题库链接
197.上升的温度
题目:
给定一个 Weather
表,编写一个 SQL 查询,来查找与之前(昨天的)日期相比温度更高的所有日期的 Id。
Id(INT) | RecordDate(DATE) | Temperature(INT) |
---|---|---|
1 | 2015-01-01 | 10 |
2 | 2015-01-02 | 25 |
3 | 2015-01-03 | 20 |
4 | 2015-01-04 | 30 |
例如,根据上述给定的 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')
解答:
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;
日期差
datediff
用法:datediff(date1, date2)
计算两个日期相差的天数,前减后(不能作用于小时或分钟)。
d1
和d2
可以是DATE
或DATETIME
类型(可以不同)。如果是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
timestampdiff
用法:timestampdiff(unit, d1, d2)
计算两个时间相差的 年/月/日/小时/分钟/秒,后减前;
d1
和d2
可以是DATE
或DATETIME
类型(可以不同)。如果是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’)。
Id | Client_Id | Driver_Id | City_Id | Status | Request_at |
---|---|---|---|---|---|
1 | 1 | 10 | 1 | completed | 2013-10-01 |
2 | 2 | 11 | 1 | cancelled_by_driver | 2013-10-01 |
3 | 3 | 12 | 6 | completed | 2013-10-01 |
4 | 4 | 13 | 6 | cancelled_by_client | 2013-10-01 |
5 | 1 | 10 | 1 | completed | 2013-10-02 |
6 | 2 | 11 | 6 | completed | 2013-10-02 |
7 | 3 | 12 | 6 | completed | 2013-10-02 |
8 | 2 | 12 | 12 | completed | 2013-10-03 |
9 | 3 | 10 | 12 | completed | 2013-10-03 |
10 | 4 | 13 | 12 | cancelled_by_driver | 2013-10-03 |
Users
表存所有用户。每个用户有唯一键 Users_Id。Banned 表示这个用户是否被禁止,Role 则是一个表示(‘client’, ‘driver’, ‘partner’)的枚举类型。
Users_Id | Banned | Role |
---|---|---|
1 | No | client |
2 | Yes | client |
3 | No | client |
4 | No | client |
10 | No | driver |
11 | No | driver |
12 | No | driver |
13 | No | driver |
写一段 SQL 语句查出 2013年10月1日 至 2013年10月3日 期间非禁止用户的取消率。基于上表,你的 SQL 语句应返回如下结果,取消率(Cancellation Rate)保留两位小数。
取消率的计算方式如下:(被司机或乘客取消的非禁止用户生成的订单数量) / (非禁止用户生成的订单总数)
Day | Cancellation Rate |
---|---|
2013-10-01 | 0.33 |
2013-10-02 | 0.00 |
2013-10-03 | 0.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')
解答:
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
表:
name | continent | area | population | gdp |
---|---|---|---|---|
Afghanistan | Asia | 652230 | 25500100 | 20343000 |
Albania | Europe | 28748 | 2831741 | 12960000 |
Algeria | Africa | 2381741 | 37100000 | 188681000 |
Andorra | Europe | 468 | 78115 | 3712000 |
Angola | Africa | 1246700 | 20609294 | 100990000 |
如果一个国家的面积超过300万平方公里,或者人口超过2500万,那么这个国家就是大国家。
编写一个SQL查询,输出表中所有大国家的名称、人口和面积。
例如,根据上表,我们应该输出:
name | population | area |
---|---|---|
Afghanistan | 25500100 | 652230 |
Algeria | 37100000 | 2381741 |
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')
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
- 数据量较大或者对多列筛选时,用
union
会更比用or
更快,or
用于多列筛选时,只有一列能用索引,其余列会全部扫描; union
会对结果去重,union all
不去重。
601.体育馆的人流量
题目:
X 市建了一个新的体育馆,每日人流量信息被记录在这三列信息中:序号 (id)、日期 (visit_date)、 人流量 (people)。
请编写一个查询语句,找出人流量的高峰期。高峰期时,至少连续三行记录中的人流量不少于100。
例如,表 stadium
:
id | visit_date | people |
---|---|---|
1 | 2017-01-01 | 10 |
2 | 2017-01-02 | 109 |
3 | 2017-01-03 | 150 |
4 | 2017-01-04 | 99 |
5 | 2017-01-05 | 145 |
6 | 2017-01-06 | 1455 |
7 | 2017-01-07 | 199 |
8 | 2017-01-08 | 188 |
对于上面的示例数据,输出为:
id | visit_date | people |
---|---|---|
5 | 2017-01-05 | 145 |
6 | 2017-01-06 | 1455 |
7 | 2017-01-07 | 199 |
8 | 2017-01-08 | 188 |
提示:
- 每天只有一行记录,日期随着 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')
解答:
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
了两次 😐 把 s1
、s2
和 s3
的符合的 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 来输出小美想要的结果呢?
示例:
id | student |
---|---|
1 | Abbot |
2 | Doris |
3 | Emerson |
4 | Green |
5 | Jeames |
假如数据输入的是上表,则输出结果如下:
id | student |
---|---|
1 | Doris |
2 | Abbot |
3 | Green |
4 | Emerson |
5 | Jeames |
注意: 如果学生人数是奇数,则不需要改变最后一个同学的座位。
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')
题解:
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=1
,1^1=0
,2^1=3
,3^1=2
SELECT id, (id+1)^1-1, student FROM seat
运行结果:
id | (id+1)^1-1 | student |
---|---|---|
1 | 2 | Abbot |
2 | 1 | Doris |
3 | 4 | Emerson |
4 | 3 | Green |
5 | 6 | Jeames |
表连接后,用 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 语句。
例如:
id | name | sex | salary |
---|---|---|---|
1 | A | m | 2500 |
2 | B | f | 1500 |
3 | C | m | 5500 |
4 | D | f | 500 |
运行你所编写的更新语句之后,将会得到以下表:
id | name | sex | salary |
---|---|---|---|
1 | A | f | 2500 |
2 | B | m | 1500 |
3 | C | f | 5500 |
4 | D | m | 500 |
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')
题解:
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 Name | Type |
---|---|
id | int |
revenue | int |
month | varchar |
(id, month) 是表的联合主键。
这个表格有关于每个部门每月收入的信息。
月份(month)可以取下列值 [“Jan”,“Feb”,“Mar”,“Apr”,“May”,“Jun”,“Jul”,“Aug”,“Sep”,“Oct”,“Nov”,“Dec”]。
编写一个 SQL 查询来重新格式化表,使得新的表中有一个部门 id 列和一些对应 每个月 的收入(revenue)列。
查询结果格式如下面的示例所示:
Department
表:
id | revenue | month |
---|---|---|
1 | 8000 | Jan |
2 | 9000 | Jan |
3 | 10000 | Feb |
1 | 7000 | Feb |
1 | 6000 | Mar |
±-----±--------±------+
id | Jan_Revenue | Feb_Revenue | Mar_Revenue | … | Dec_Revenue |
---|---|---|---|---|---|
1 | 8000 | 7000 | 6000 | … | null |
2 | 9000 | null | null | … | null |
3 | null | 10000 | null | … | null |
注意,结果表有 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')
题解:
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对应多个月份,要用聚合函数取出想要的月份。