Leetcode sql题目记录
文章目录
- Leetcode sql题目记录
- 570. 至少有5名直接下属的经理
- 1934. 确认率
- 1193. 每月交易I
- 1174. 即时食物配送II
- 176. 第二高的薪水
- (1) 子查询为空但外层用了聚合函数
- (2)子查询为空而外层没有聚合函数
- 550. 游戏玩法分析IV
- 1045. 买下所有产品的客户
- 180. 连续出现的数字
- 1164. 指定日期的产品价格
- (1)非相关子查询 + 等值比较
- (2)相关子查询
- (3)窗口函数 + 条件筛选
- (4)ORDER BY + LIMIT
- 对比总结
- 1204.最后一个能进入巴士的人
- (1)子查询+等值过滤
- (2)ORDER BY+LIMIT
- 1907. 按分类统计薪水
- 626. 换座位
- (1)嵌套查询
- (2)CASE WHEN
- a. 简单 CASE
- b. 搜索 CASE (最常用)
- c. 作为一个字段输出
- d. 在聚合函数里做条件统计
- e. 排序(ORDER BY)
本博客仅记录高频 SQL 50 题(基础版)的中等难度题目 苯人的解答与学习过程,后续不定期更新
570. 至少有5名直接下属的经理
表: Employee
+-------------+---------+
| Column Name | Type |
+-------------+---------+
| id | int |
| name | varchar |
| department | varchar |
| managerId | int |
+-------------+---------+
id 是此表的主键(具有唯一值的列)。
该表的每一行表示雇员的名字、他们的部门和他们的经理的id。
如果managerId为空,则该员工没有经理。
没有员工会成为自己的管理者。
编写一个解决方案,找出至少有五个直接下属的经理。
以 任意顺序 返回结果表。
查询结果格式如下所示。
示例 1:
输入:
Employee 表:
+-----+-------+------------+-----------+
| id | name | department | managerId |
+-----+-------+------------+-----------+
| 101 | John | A | Null |
| 102 | Dan | A | 101 |
| 103 | James | A | 101 |
| 104 | Amy | A | 101 |
| 105 | Anne | A | 101 |
| 106 | Ron | B | 101 |
+-----+-------+------------+-----------+
输出:
+------+
| name |
+------+
| John |
+------+
解答:
select a.name from Employee a join Employee b on a.id = b.managerId group by a.id having count(*) >= 5;
没有 GROUP BY
HAVING
是对分组后的结果过滤的。你没分组时,MySQL把整张结果集当成一个大组,COUNT(a.id)
统计的是所有经理–下属配对的总行数,不是“每个经理的下属数”。因此当测试数据里只有一个经理且刚好有 5 个下属时看起来“正确”,但只要有两个经理,合计行数 ≥5,你这句就会把两位经理都放进结果里,甚至还会重复多行。
1934. 确认率
表: Signups
+----------------+----------+
| Column Name | Type |
+----------------+----------+
| user_id | int |
| time_stamp | datetime |
+----------------+----------+
User_id是该表的主键。
每一行都包含ID为user_id的用户的注册时间信息。
表: Confirmations
+----------------+----------+
| Column Name | Type |
+----------------+----------+
| user_id | int |
| time_stamp | datetime |
| action | ENUM |
+----------------+----------+
(user_id, time_stamp)是该表的主键。
user_id是一个引用到注册表的外键。
action是类型为('confirmed', 'timeout')的ENUM
该表的每一行都表示ID为user_id的用户在time_stamp请求了一条确认消息,该确认消息要么被确认('confirmed'),要么被过期('timeout')。
用户的 确认率 是 'confirmed'
消息的数量除以请求的确认消息的总数。没有请求任何确认消息的用户的确认率为 0
。确认率四舍五入到 小数点后两位 。
编写一个SQL查询来查找每个用户的 确认率 。
以 任意顺序 返回结果表。
查询结果格式如下所示。
示例1:
输入:
Signups 表:
+---------+---------------------+
| user_id | time_stamp |
+---------+---------------------+
| 3 | 2020-03-21 10:16:13 |
| 7 | 2020-01-04 13:57:59 |
| 2 | 2020-07-29 23:09:44 |
| 6 | 2020-12-09 10:39:37 |
+---------+---------------------+
Confirmations 表:
+---------+---------------------+-----------+
| user_id | time_stamp | action |
+---------+---------------------+-----------+
| 3 | 2021-01-06 03:30:46 | timeout |
| 3 | 2021-07-14 14:00:00 | timeout |
| 7 | 2021-06-12 11:57:29 | confirmed |
| 7 | 2021-06-13 12:58:28 | confirmed |
| 7 | 2021-06-14 13:59:27 | confirmed |
| 2 | 2021-01-22 00:00:00 | confirmed |
| 2 | 2021-02-28 23:59:59 | timeout |
+---------+---------------------+-----------+
输出:
+---------+-------------------+
| user_id | confirmation_rate |
+---------+-------------------+
| 6 | 0.00 |
| 3 | 0.00 |
| 7 | 1.00 |
| 2 | 0.50 |
+---------+-------------------+
解释:
用户 6 没有请求任何确认消息。确认率为 0。
用户 3 进行了 2 次请求,都超时了。确认率为 0。
用户 7 提出了 3 个请求,所有请求都得到了确认。确认率为 1。
用户 2 做了 2 个请求,其中一个被确认,另一个超时。确认率为 1 / 2 = 0.5。
解答:
WITH base AS (SELECT user_id,COUNT(*) AS total,SUM(CASE WHEN action = 'confirmed' THEN 1 ELSE 0 END) AS confirmedFROM ConfirmationsGROUP BY user_id
)
SELECT s.user_id,ROUND(IFNULL(b.confirmed / b.total, 0), 2) AS confirmation_rate
FROM Signups s
LEFT JOIN base bON s.user_id = b.user_id;
1193. 每月交易I
表:Transactions
+---------------+---------+
| Column Name | Type |
+---------------+---------+
| id | int |
| country | varchar |
| state | enum |
| amount | int |
| trans_date | date |
+---------------+---------+
id 是这个表的主键。
该表包含有关传入事务的信息。
state 列类型为 ["approved", "declined"] 之一。
编写一个 sql 查询来查找每个月和每个国家/地区的事务数及其总金额、已批准的事务数及其总金额。
以 任意顺序 返回结果表。
查询结果格式如下所示。
示例 1:
输入:
Transactions table:
+------+---------+----------+--------+------------+
| id | country | state | amount | trans_date |
+------+---------+----------+--------+------------+
| 121 | US | approved | 1000 | 2018-12-18 |
| 122 | US | declined | 2000 | 2018-12-19 |
| 123 | US | approved | 2000 | 2019-01-01 |
| 124 | DE | approved | 2000 | 2019-01-07 |
+------+---------+----------+--------+------------+
输出:
+----------+---------+-------------+----------------+--------------------+-----------------------+
| month | country | trans_count | approved_count | trans_total_amount | approved_total_amount |
+----------+---------+-------------+----------------+--------------------+-----------------------+
| 2018-12 | US | 2 | 1 | 3000 | 1000 |
| 2019-01 | US | 1 | 1 | 2000 | 2000 |
| 2019-01 | DE | 1 | 1 | 2000 | 2000 |
+----------+---------+-------------+----------------+--------------------+-----------------------+
解答:
多个 CTE 用 WITH a AS (...), b AS (...)
。
按 month、country
分组;approved
相关用 CASE WHEN
计数/求和。
取月份用 DATE_FORMAT(trans_date, '%Y-%m')
更稳妥。
WITH base AS (SELECTDATE_FORMAT(trans_date, '%Y-%m') AS month,country,state,amountFROM Transactions
),
seq AS (SELECTmonth,country,COUNT(*) AS trans_count,SUM(CASE WHEN state = 'approved' THEN 1 ELSE 0 END) AS approved_count,SUM(amount) AS trans_total_amount,SUM(CASE WHEN state = 'approved' THEN amount ELSE 0 END) AS approved_total_amountFROM baseGROUP BY month, country
)
SELECT *
FROM seq;
1174. 即时食物配送II
配送表: Delivery
+-----------------------------+---------+
| Column Name | Type |
+-----------------------------+---------+
| delivery_id | int |
| customer_id | int |
| order_date | date |
| customer_pref_delivery_date | date |
+-----------------------------+---------+
delivery_id 是该表中具有唯一值的列。
该表保存着顾客的食物配送信息,顾客在某个日期下了订单,并指定了一个期望的配送日期(和下单日期相同或者在那之后)。
如果顾客期望的配送日期和下单日期相同,则该订单称为 「即时订单」,否则称为「计划订单」。
「首次订单」是顾客最早创建的订单。我们保证一个顾客只会有一个「首次订单」。
编写解决方案以获取即时订单在所有用户的首次订单中的比例。保留两位小数。
结果示例如下所示:
示例 1:
输入:
Delivery 表:
+-------------+-------------+------------+-----------------------------+
| delivery_id | customer_id | order_date | customer_pref_delivery_date |
+-------------+-------------+------------+-----------------------------+
| 1 | 1 | 2019-08-01 | 2019-08-02 |
| 2 | 2 | 2019-08-02 | 2019-08-02 |
| 3 | 1 | 2019-08-11 | 2019-08-12 |
| 4 | 3 | 2019-08-24 | 2019-08-24 |
| 5 | 3 | 2019-08-21 | 2019-08-22 |
| 6 | 2 | 2019-08-11 | 2019-08-13 |
| 7 | 4 | 2019-08-09 | 2019-08-09 |
+-------------+-------------+------------+-----------------------------+
输出:
+----------------------+
| immediate_percentage |
+----------------------+
| 50.00 |
+----------------------+
解释:
1 号顾客的 1 号订单是首次订单,并且是计划订单。
2 号顾客的 2 号订单是首次订单,并且是即时订单。
3 号顾客的 5 号订单是首次订单,并且是计划订单。
4 号顾客的 7 号订单是首次订单,并且是即时订单。
因此,一半顾客的首次订单是即时的。
解答:
with base as (
select *, row_number() over(partition by customer_id order by order_date) as rn
from Delivery),
seq as (select round((sum(case when customer_pref_delivery_date = order_date then 1 else 0 end) / count(*)) * 100, 2) as immediate_percentage from base where rn = 1
)
select * from seq;
176. 第二高的薪水
Employee
表:
+-------------+------+
| Column Name | Type |
+-------------+------+
| id | int |
| salary | int |
+-------------+------+
id 是这个表的主键。
表的每一行包含员工的工资信息。
查询并返回 Employee
表中第二高的 不同 薪水 。如果不存在第二高的薪水,查询应该返回 null(Pandas 则返回 None)
。
查询结果如下例所示。
示例 1:
输入:
Employee 表:
+----+--------+
| id | salary |
+----+--------+
| 1 | 100 |
| 2 | 200 |
| 3 | 300 |
+----+--------+
输出:
+---------------------+
| SecondHighestSalary |
+---------------------+
| 200 |
+---------------------+
示例 2:
输入:
Employee 表:
+----+--------+
| id | salary |
+----+--------+
| 1 | 100 |
+----+--------+
输出:
+---------------------+
| SecondHighestSalary |
+---------------------+
| null |
+---------------------+
解答
SELECT MAX(salary) AS SecondHighestSalary
FROM (SELECT DISTINCT salary,DENSE_RANK() OVER (ORDER BY salary DESC) AS rkFROM Employee
) t
WHERE rk = 2;
(1) 子查询为空但外层用了聚合函数
SQL 的定义是:聚合函数对空输入时,返回 NULL。
例子:
SELECT MAX(salary) FROM (SELECT 1 AS salary WHERE 1=0) t;
(SELECT 1 WHERE 1=0)
为空表 → MAX
没有任何值可比较 → 返回 NULL
。
所以这就是为什么“第二高薪水不存在”时 MAX
可以帮我们自动返回 NULL
。
(2)子查询为空而外层没有聚合函数
如果你直接:
SELECT salary
FROM (SELECT salary WHERE 1=0) t;
那结果就是 0 行(即直接返回空结果集,不会自动给你一行 NULL
)。
550. 游戏玩法分析IV
Table: Activity
+--------------+---------+
| Column Name | Type |
+--------------+---------+
| player_id | int |
| device_id | int |
| event_date | date |
| games_played | int |
+--------------+---------+
(player_id,event_date)是此表的主键(具有唯一值的列的组合)。
这张表显示了某些游戏的玩家的活动情况。
每一行是一个玩家的记录,他在某一天使用某个设备注销之前登录并玩了很多游戏(可能是 0)。
编写解决方案,报告在首次登录的第二天再次登录的玩家的 比率,四舍五入到小数点后两位。换句话说,你需要计算从首次登录后的第二天登录的玩家数量,并将其除以总玩家数。
结果格式如下所示:
示例 1:
输入:
Activity table:
+-----------+-----------+------------+--------------+
| player_id | device_id | event_date | games_played |
+-----------+-----------+------------+--------------+
| 1 | 2 | 2016-03-01 | 5 |
| 1 | 2 | 2016-03-02 | 6 |
| 2 | 3 | 2017-06-25 | 1 |
| 3 | 1 | 2016-03-02 | 0 |
| 3 | 4 | 2018-07-03 | 5 |
+-----------+-----------+------------+--------------+
输出:
+-----------+
| fraction |
+-----------+
| 0.33 |
+-----------+
解释:
只有 ID 为 1 的玩家在第一天登录后才重新登录,所以答案是 1/3 = 0.33
解答:
# Write your MySQL query statement below
with base as(select *,min(event_date) over(partition by player_id)as first from Activity
),
seq as(select player_id, sum(case when datediff(event_date, first) = 1 then 1 else 0 end) as rn from basegroup by player_id
)
select round(IFNULL((sum(rn) / count(distinct player_id)), 0), 2) as fraction from seq;
注意:
- 在使用with base as(), seq as() select from seq时,要记得每个嵌套内部的select很重要,前一个select需要包含后一个select的内容。
- 使用聚合函数min,max,count后结果会被压缩,如果后续要同行对最大、最小进行比较,要使用窗口函数形式的max,min…
1045. 买下所有产品的客户
Customer
表:
+-------------+---------+
| Column Name | Type |
+-------------+---------+
| customer_id | int |
| product_key | int |
+-------------+---------+
该表可能包含重复的行。
customer_id 不为 NULL。
product_key 是 Product 表的外键(reference 列)。
Product
表:
+-------------+---------+
| Column Name | Type |
+-------------+---------+
| product_key | int |
+-------------+---------+
product_key 是这张表的主键(具有唯一值的列)。
编写解决方案,报告 Customer
表中购买了 Product
表中所有产品的客户的 id。
返回结果表 无顺序要求 。
返回结果格式如下所示。
示例 1:
输入:
Customer 表:
+-------------+-------------+
| customer_id | product_key |
+-------------+-------------+
| 1 | 5 |
| 2 | 6 |
| 3 | 5 |
| 3 | 6 |
| 1 | 6 |
+-------------+-------------+
Product 表:
+-------------+
| product_key |
+-------------+
| 5 |
| 6 |
+-------------+
输出:
+-------------+
| customer_id |
+-------------+
| 1 |
| 3 |
+-------------+
解释:
购买了所有产品(5 和 6)的客户的 id 是 1 和 3 。
解答:
WHERE DISTINCT
是非法语法。DISTINCT
只能放在SELECT
或COUNT(DISTINCT ...)
里面,不能直接放在WHERE
。- 题目要求的是“买了所有产品的顾客”,所以应该比较顾客买到的 去重后产品数 和 Product 表里的产品总数。
SELECT customer_id
FROM Customer
GROUP BY customer_id
HAVING COUNT(DISTINCT product_key) = (SELECT COUNT(*) FROM Product
);
解释一下:
GROUP BY customer_id
:按顾客分组。COUNT(DISTINCT product_key)
:统计该顾客买了多少种不同的产品。- 子查询
(SELECT COUNT(*) FROM Product)
:统计总共有多少种产品。 HAVING ... = ...
:筛选出买全所有产品的顾客。
180. 连续出现的数字
表:Logs
+-------------+---------+
| Column Name | Type |
+-------------+---------+
| id | int |
| num | varchar |
+-------------+---------+
在 SQL 中,id 是该表的主键。
id 是一个自增列。
找出所有至少连续出现三次的数字。
返回的结果表中的数据可以按 任意顺序 排列。
结果格式如下面的例子所示:
示例 1:
输入:
Logs 表:
+----+-----+
| id | num |
+----+-----+
| 1 | 1 |
| 2 | 1 |
| 3 | 1 |
| 4 | 2 |
| 5 | 1 |
| 6 | 2 |
| 7 | 2 |
+----+-----+
输出:
Result 表:
+-----------------+
| ConsecutiveNums |
+-----------------+
| 1 |
+-----------------+
解释:1 是唯一连续出现至少三次的数字。
解答:
with base as(select *, row_number() over(partition by num order by id) as rn from Logs
),
seq as(select *, id - rn as diff from base
),
rpq as (select num, count(*) as ct from seq group by num, diff having count(*) >= 3
)
select distinct num as ConsecutiveNums from rpq;
有时候不要忘记distinct
1164. 指定日期的产品价格
产品数据表: Products
+---------------+---------+
| Column Name | Type |
+---------------+---------+
| product_id | int |
| new_price | int |
| change_date | date |
+---------------+---------+
(product_id, change_date) 是此表的主键(具有唯一值的列组合)。
这张表的每一行分别记录了 某产品 在某个日期 更改后 的新价格。
一开始,所有产品价格都为 10。
编写一个解决方案,找出在 2019-08-16
所有产品的价格。
以 任意顺序 返回结果表。
结果格式如下例所示。
示例 1:
输入:
Products 表:
+------------+-----------+-------------+
| product_id | new_price | change_date |
+------------+-----------+-------------+
| 1 | 20 | 2019-08-14 |
| 2 | 50 | 2019-08-14 |
| 1 | 30 | 2019-08-15 |
| 1 | 35 | 2019-08-16 |
| 2 | 65 | 2019-08-17 |
| 3 | 20 | 2019-08-18 |
+------------+-----------+-------------+
输出:
+------------+-------+
| product_id | price |
+------------+-------+
| 2 | 50 |
| 1 | 35 |
| 3 | 10 |
+------------+-------+
解答:
with base as(select *, row_number()over (partition by product_id order by change_date DESC) as rn from Products where change_date <= '2019-08-16'
),
seq as (select product_id, new_price as price from base where rn = 1
)
select product_id, price from seq union all select distinct product_id, 10 as price from (select product_id, min(change_date) as mi from Products group by product_id
)t where mi > '2019-08-16';
归纳:
想要得到某一列的最大最小值,并用其与某个数进行比较,需要先max、min(窗口或者聚合函数),然后再等值查询,或者rownumber配合ASC、DESC,然后再后续查询中使用rn=1,或者直接使用order by+limit
(1)非相关子查询 + 等值比较
使用场景:全局只有一个最值(整个表的最大值/最小值),不依赖分组。
- 特点:子查询返回单行,可以直接用
=
比较。 - 示例:找出工资最高的员工
select *
from Employee
where salary = (select max(salary) from Employee);
(2)相关子查询
使用场景:分组后的最值(每个客户/部门的最小/最大日期),外层表的条件依赖子查询。
- 特点:子查询里需要引用外层的列,每行执行一次,保证返回单行。
- 示例:找每个客户的首单
select *
from Delivery d1
where order_date = (select min(order_date)from Delivery d2where d2.customer_id = d1.customer_id
);
这里子查询里的 d2.customer_id = d1.customer_id
绑定了外层查询的 d1.customer_id
。
每次外层取一行,子查询就只计算该客户的最小订单日期。
也可以写成这种形式:
select *
from (select customer_id, min(order_date)from Delivery group by customer_id
)t ;
(3)窗口函数 + 条件筛选
使用场景:数据库支持窗口函数(MySQL 8+ / PostgreSQL / Oracle / SQL Server),需要按组取前几名/最值。
也可以使用max、min的窗口函数形式来获取每个组别的最值
- 特点:写法简洁,可同时保留分组内排序信息。
- 示例:找每个客户的首单
with t as (select *,row_number() over(partition by customer_id order by order_date) as rnfrom Delivery
)
select *
from t
where rn = 1;
- 如果要处理并列情况,可以用
rank()
或dense_rank()
。
(4)ORDER BY + LIMIT
使用场景:只需要全表范围内的最值,且只取前 N 条(不分组)。
- 特点:简单高效,但无法直接应对「每组最值」问题。
- 示例:找工资最高的员工(Top 1)
select *
from Employee
order by salary desc
limit 1;
- 如果需要每组最值,就不适合,只能配合窗口函数或子查询。
对比总结
方法 | 适用场景 | 优点 | 局限 |
---|---|---|---|
非相关子查询 | 全表唯一最值 | 简单 | 只能处理单个全局最值 |
相关子查询 | 每组最值(需要依赖外层条件) | 通用,不要求窗口函数 | 子查询多次执行,性能较差 |
窗口函数 | 每组最值/前 N 个 | 高效简洁,现代 SQL 推荐 | 需要数据库支持窗口函数 |
ORDER BY + LIMIT | 全表前 N 个 | 简洁高效 | 不能分组,只能处理全局最值 |
👉 快速记忆:
- 全局最值 → 非相关子查询 或
order by + limit
- 分组最值 → 相关子查询 或 窗口函数
- 要前 N 名/并列情况 → 窗口函数最佳
1204.最后一个能进入巴士的人
表: Queue
+-------------+---------+
| Column Name | Type |
+-------------+---------+
| person_id | int |
| person_name | varchar |
| weight | int |
| turn | int |
+-------------+---------+
person_id 是这个表具有唯一值的列。
该表展示了所有候车乘客的信息。
表中 person_id 和 turn 列将包含从 1 到 n 的所有数字,其中 n 是表中的行数。
turn 决定了候车乘客上巴士的顺序,其中 turn=1 表示第一个上巴士,turn=n 表示最后一个上巴士。
weight 表示候车乘客的体重,以千克为单位。
有一队乘客在等着上巴士。然而,巴士有1000
千克 的重量限制,所以其中一部分乘客可能无法上巴士。
编写解决方案找出 最后一个 上巴士且不超过重量限制的乘客,并报告 person_name
。题目测试用例确保顺位第一的人可以上巴士且不会超重。
返回结果格式如下所示。
示例 1:
输入:
Queue 表
+-----------+-------------+--------+------+
| person_id | person_name | weight | turn |
+-----------+-------------+--------+------+
| 5 | Alice | 250 | 1 |
| 4 | Bob | 175 | 5 |
| 3 | Alex | 350 | 2 |
| 6 | John Cena | 400 | 3 |
| 1 | Winston | 500 | 6 |
| 2 | Marie | 200 | 4 |
+-----------+-------------+--------+------+
输出:
+-------------+
| person_name |
+-------------+
| John Cena |
+-------------+
解释:
为了简化,Queue 表按 turn 列由小到大排序。
+------+----+-----------+--------+--------------+
| Turn | ID | Name | Weight | Total Weight |
+------+----+-----------+--------+--------------+
| 1 | 5 | Alice | 250 | 250 |
| 2 | 3 | Alex | 350 | 600 |
| 3 | 6 | John Cena | 400 | 1000 | (最后一个上巴士)
| 4 | 2 | Marie | 200 | 1200 | (无法上巴士)
| 5 | 4 | Bob | 175 | ___ |
| 6 | 1 | Winston | 500 | ___ |
+------+----+-----------+--------+--------------+
解答:
PS:SQL 聚合函数(如 MAX()
)和普通列之间没有自动的“行对齐”关系。
不能直接写成!:
select person_name, max(total_weight) from (select * , sum(weight)over (order by turn) as total_weight from Queue
)t where total_weight <= 1000;-- 结果只会返回
| person_id | person_name | weight | turn |
| --------- | ----------- | ------ | ---- |
| 5 | Alice | 250 | 1 |
| 4 | Bob | 175 | 5 |
| 3 | Alex | 350 | 2 |
| 6 | John Cena | 400 | 3 |
| 1 | Winston | 500 | 6 |
| 2 | Marie | 200 | 4 || person_name | max(total_weight) |
| ----------- | ----------------- |
| Alice | 1000 |-- 而不是正确输出
| person_name |
| ----------- |
| John Cena |
(1)子查询+等值过滤
# Write your MySQL query statement below
with base as(select * , sum(weight)over (order by turn) as total_weight from Queue
)
select person_name from base where total_weight = (select max(total_weight) as mx from base where total_weight <= 1000
);
(2)ORDER BY+LIMIT
# Write your MySQL query statement below
select person_name from (select * , sum(weight)over (order by turn) as total_weight from Queue
)t where total_weight <= 1000 order by total_weight DESC limit 1;
1907. 按分类统计薪水
表: Accounts
+-------------+------+
| 列名 | 类型 |
+-------------+------+
| account_id | int |
| income | int |
+-------------+------+
在 SQL 中,account_id 是这个表的主键。
每一行都包含一个银行帐户的月收入的信息。
查询每个工资类别的银行账户数量。 工资类别如下:
"Low Salary"
:所有工资 严格低于20000
美元。"Average Salary"
: 包含 范围内的所有工资[$20000, $50000]
。"High Salary"
:所有工资 严格大于50000
美元。
结果表 必须 包含所有三个类别。 如果某个类别中没有帐户,则报告 0
。
按 任意顺序 返回结果表。
查询结果格式如下示例。
示例 1:
输入:
Accounts 表:
+------------+--------+
| account_id | income |
+------------+--------+
| 3 | 108939 |
| 2 | 12747 |
| 8 | 87709 |
| 6 | 91796 |
+------------+--------+
输出:
+----------------+----------------+
| category | accounts_count |
+----------------+----------------+
| Low Salary | 1 |
| Average Salary | 0 |
| High Salary | 3 |
+----------------+----------------+
解释:
低薪: 有一个账户 2.
中等薪水: 没有.
高薪: 有三个账户,他们是 3, 6和 8.
解答:
select 'Low Salary' as category, sum(case when income < 20000 then 1 else 0 end) as accounts_count
from Accounts
Union all
select 'Average Salary' as category, sum(case when income >= 20000 and income <= 50000 then 1 else 0 end) as accounts_count
from Accounts
Union all
select 'High Salary' as category, sum(case when income > 50000 then 1 else 0 end) as accounts_count
from Accounts;
626. 换座位
表: Seat
+-------------+---------+
| Column Name | Type |
+-------------+---------+
| id | int |
| student | varchar |
+-------------+---------+
id 是该表的主键(唯一值)列。
该表的每一行都表示学生的姓名和 ID。
ID 序列始终从 1 开始并连续增加。
编写解决方案来交换每两个连续的学生的座位号。如果学生的数量是奇数,则最后一个学生的id不交换。
按 id
升序 返回结果表。
查询结果格式如下所示。
示例 1:
输入:
Seat 表:
+----+---------+
| id | student |
+----+---------+
| 1 | Abbot |
| 2 | Doris |
| 3 | Emerson |
| 4 | Green |
| 5 | Jeames |
+----+---------+
输出:
+----+---------+
| id | student |
+----+---------+
| 1 | Doris |
| 2 | Abbot |
| 3 | Green |
| 4 | Emerson |
| 5 | Jeames |
+----+---------+
解释:
请注意,如果学生人数为奇数,则不需要更换最后一名学生的座位。
解答:
(1)嵌套查询
# Write your MySQL query statement below
with base as(select *, lead(student, 1)over (order by id) as exchange_nxt, lag(student, 1)over (order by id) as exchange_pre from Seat
)
select id, IFNULL((case when (id % 2) != 0 then exchange_nxt else exchange_pre end), student) as student from base;
(2)CASE WHEN
# Write your MySQL query statement below
select case when id % 2 = 1 and id < (select max(id) from Seat)then id + 1when id % 2 = 0 then id - 1else idend as id, studentfrom Seat
order by id;
在 SQL 里,CASE
可以写两种形式:
a. 简单 CASE
直接对某个表达式的值做匹配:
CASE column_nameWHEN 'A' THEN '类型1'WHEN 'B' THEN '类型2'ELSE '其他'
END
b. 搜索 CASE (最常用)
条件判断灵活,可以写比较、范围等:
CASEWHEN score >= 90 THEN '优秀'WHEN score >= 60 THEN '及格'ELSE '不及格'
END
c. 作为一个字段输出
SELECT name,CASE WHEN age < 18 THEN '未成年'WHEN age < 60 THEN '成年人'ELSE '老年人'END AS age_group
FROM users;
d. 在聚合函数里做条件统计
SELECTSUM(CASE WHEN status = 'success' THEN 1 ELSE 0 END) AS success_count,SUM(CASE WHEN status = 'fail' THEN 1 ELSE 0 END) AS fail_count
FROM logs;
常用于 条件计数。
e. 排序(ORDER BY)
SELECT *
FROM orders
ORDER BYCASE WHEN status = 'vip' THEN 1WHEN status = 'normal' THEN 2ELSE 3END;
补充:
SQL执行顺序:
- FROM Employee
- WHERE salary > 5000 (先筛掉工资不够的行)
- GROUP BY dept (按部门分组)
- COUNT(*) 统计每组人数
- HAVING 过滤出人数 ≥3 的部门
- SELECT 输出 dept, cnt
- ORDER BY 按 cnt 降序
- LIMIT 取前 5 个部门