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 個部門