目錄
1393. 股票的資本損益🔒
https://leetcode-cn.com/problems/capital-gainloss/
(stock_name, day) 是這張表的主鍵,operation 列使用的是一種枚舉類型,包括:('Sell','Buy'),此表的每一行代表了名爲 stock_name 的某支股票在 operation_day 這一天的操作價格。保證股票的每次'Sell'操作前,都有相應的'Buy'操作。
編寫一個SQL查詢來報告每支股票的資本損益。股票的資本損益是一次或多次買賣股票後的全部收益或損失。以任意順序返回結果即可。
SQL查詢結果的格式如下例所示:
Leetcode 股票在第一天以1000美元的價格買入,在第五天以9000美元的價格賣出。資本收益=9000-1000=8000美元。
Handbags 股票在第17天以30000美元的價格買入,在第29天以7000美元的價格賣出。資本損失=7000-30000=-23000美元。
Corona Masks 股票在第1天以10美元的價格買入,在第3天以1010美元的價格賣出。在第4天以1000美元的價格再次購買,在第5天以500美元的價格出售。最後,它在第6天以1000美元的價格被買走,在第10天以10000美元的價格被賣掉。資本損益是每次(’Buy'->'Sell')操作資本收益或損失的和=(1010-10)+(500-1000)+(10000-1000)=1000-500+9000=9500美元。
題解
一:case when的使用。
select stock_name,
sum(case when operation="Buy" then -1 * price
else price end) capital_gain_loss
from Stocks
group by stock_name
1270. 向公司CEO彙報工作的所有人🔒
https://leetcode-cn.com/problems/all-people-report-to-the-given-manager/
employee_id 是這個表的主鍵。這個表中每一行中,employee_id 表示職工的 ID,employee_name 表示職工的名字,manager_id 表示該職工彙報工作的直線經理。這個公司 CEO 是 employee_id = 1 的人。
用 SQL 查詢出所有直接或間接向公司 CEO 彙報工作的職工的 employee_id 。由於公司規模較小,經理之間的間接關係不超過 3 個經理。可以以任何順序返回的結果,不需要去重。查詢結果示例如下:
公司 CEO 的 employee_id 是 1.
employee_id 是 2 和 77 的職員直接彙報給公司 CEO。
employee_id 是 4 的職員間接彙報給公司 CEO 4 --> 2 --> 1 。
employee_id 是 7 的職員間接彙報給公司 CEO 7 --> 4 --> 2 --> 1 。
employee_id 是 3, 8 ,9 的職員不會直接或間接的彙報給公司 CEO。
題解
一:三張表聯合,因爲題目中說了由於公司規模較小,經理之間的間接關係不超過 3 個經理,只要第三張表的manager_id是1即可。
select c.employee_id from
(select a.employee_id, b.manager_id from Employees a
left join Employees b
on a.manager_id = b.employee_id
where a.employee_id != 1)c
left join Employees d
on c.manager_id = d.employee_id
where c.manager_id = 1 or d.manager_id = 1
二:三表聯合,簡化一下
select a.employee_id from Employees a
left join Employees b on a.manager_id = b.employee_id
left join Employees c on b.manager_id = c.employee_id
where a.employee_id != 1 and c.manager_id = 1
1398. 購買了產品A和產品B卻沒有購買產品C的顧客🔒
https://leetcode-cn.com/problems/customers-who-bought-products-a-and-b-but-not-c/
customer_id 是這張表的主鍵。 customer_name 是顧客的名稱。
order_id 是這張表的主鍵。customer_id 是購買了名爲 "product_name" 產品顧客的id。
請你設計 SQL 查詢來報告購買了產品 A 和產品 B 卻沒有購買產品 C 的顧客的 ID 和姓名( customer_id 和 customer_name ),我們將基於此結果爲他們推薦產品 C 。您返回的查詢結果需要按照 customer_id 排序。查詢結果如下例所示。
只有 customer_id 爲 3 的顧客購買了產品 A 和產品 B ,卻沒有購買產品 C 。
題解
一:兩張表聯合,加group by 函數
select o.customer_id, customer_name from Orders o
left join Customers c
on o.customer_id = c.customer_id
group by customer_id having count(product_name="C" or null) = 0
and count(product_name="B" or null) > 0 and count(product_name="A" or null) > 0
order by o.customer_id
1285. 找到連續區間的開始和結束數字🔒
https://leetcode-cn.com/problems/find-the-start-and-end-number-of-continuous-ranges/
id 是上表的主鍵。上表的每一行包含日誌表中的一個 ID。
後來一些 ID 從 Logs 表中刪除。編寫一個 SQL 查詢得到 Logs 表中的連續區間的開始數字和結束數字。將查詢表按照 start_id 排序。查詢結果格式如下面的例子:
題解
思路:
從結果樣例來看,分別需要輸出連續數列的頭和尾。
那麼,我們需要加工並獲取一個連續數列的頭,以及其對應的數列(如1的123,後續用max來取尾巴)或者直接是數列的尾巴。另外還要面對10這種單個數字存在。
這裏分別獲取頭和尾比較簡單:
數列的頭的獲取:select log_id from logs where log_id-1 not in (select * from logs)
可以得到1,7,10. 因爲連續數列中 前一個不存在的話,就是頭了(也包括了單個數字的情況)
數列的尾的獲取:select log_id from logs where log_id+1 not in (select * from logs)
可以得到3,8,10. 因爲連續數列中 後一個不存在的話,就是尾了(也包括了單個數字的情況)
自連接+where獲取結果:
如果讓前兩行數據進行笛卡爾積,並且限制尾的數字必須不小於頭的數字的話,就可以輕鬆得出結果。
select a.log_id start_id, b.log_id end_id from
(select log_id from logs where log_id-1 not in (select * from logs))a,
(select log_id from logs where log_id+1 not in (select * from logs))b
where b.log_id >= a.log_id
笛卡爾積之後的結果如下,均是選取的start_id< end_id 的pair對,我們可以發現start_id中1出現了3次,7出現了2次,但是result表中只出現了一次,這個可以通過group by分組解決,那麼end_id又如何取,我們只需要取每一個start_id分組後對應的最小的id即可。
select a.log_id start_id, min(b.log_id) end_id from
(select log_id from logs where log_id-1 not in (select * from logs))a,
(select log_id from logs where log_id+1 not in (select * from logs))b
where b.log_id >= a.log_id
group by a.log_id
二:轉自https://leetcode-cn.com/problems/find-the-start-and-end-number-of-continuous-ranges/solution/liang-ge-bian-liang-jie-jue-wen-ti-by-qbuztjk3xg/,定義兩個變量@num,@id。@id用來存放log_id,@num用來標記log_id連續的記錄。
SELECT log_id, CASE WHEN @id = log_id - 1 THEN @num := @num ELSE @num := @num + 1 END cnt, @id := log_id FROM LOGS, (SELECT @num := 0, @id := NULL) a
結果如下:
得到上述結果後,用cnt字段分組,最小log_id爲start_id,最大log_id爲end_id。
select min(log_id) start_id, max(log_id) end_id from (SELECT log_id, CASE WHEN @id = log_id - 1 THEN @num := @num ELSE @num := @num + 1 END cnt, @id := log_id FROM LOGS, (SELECT @num := 0, @id := NULL) a) b group by cnt
1308. 不同性別每日分數總計🔒
https://leetcode-cn.com/problems/running-total-for-different-genders/
(gender, day)是該表的主鍵。一場比賽是在女隊和男隊之間舉行的。該表的每一行表示一個名叫 (player_name) 性別爲 (gender) 的參賽者在某一天獲得了 (score_points) 的分數。如果參賽者是女性,那麼 gender 列爲 'F',如果參賽者是男性,那麼 gender 列爲 'M'
寫一條SQL語句查詢每種性別在每一天的總分,並按性別和日期對查詢結果排序。下面是查詢結果格式的例子:
女性隊伍:
第一天是 2019-12-30,Priyanka 獲得 17 分,隊伍的總分是 17 分
第二天是 2019-12-31, Priya 獲得 23 分,隊伍的總分是 40 分
第三天是 2020-01-01, Aron 獲得 17 分,隊伍的總分是 57 分
第四天是 2020-01-07, Alice 獲得 23 分,隊伍的總分是 80 分
男性隊伍:
第一天是 2019-12-18, Jose 獲得 2 分,隊伍的總分是 2 分
第二天是 2019-12-25, Khali 獲得 11 分,隊伍的總分是 13 分
第三天是 2019-12-30, Slaman 獲得 13 分,隊伍的總分是 26 分
第四天是 2019-12-31, Joe 獲得 3 分,隊伍的總分是 29 分
第五天是 2020-01-07, Bajrang 獲得 7 分,隊伍的總分是 36 分
題解
一:變量+分組,這邊主要解決的應該是如何累加求和的方法,此處用變量進行求解,變量m_score表示截止當前日期的男性隊伍的累加和,變量f_score表示截止當前日期的女性隊伍的累加和。
select gender, day, sum(score_points) score from Scores
group by gender, day order by gender, day
select gender, day,
case when gender = "F" then @f_score := score + @f_score
else @m_score := score + @m_score end mf_score,
case when gender = "F" then @f_score
else @m_score end total
from (select gender, day, sum(score_points) score
from Scores group by gender, day order by gender, day)a ,
(select @f_score:=0, @m_score:=0)b
select gender, day, cast(total as unsigned) total from
(select gender, day,
case when gender = "F" then @f_score := score + @f_score
else @m_score := score + @m_score end mf_score,
case when gender = "F" then @f_score
else @m_score end total
from (select gender, day, sum(score_points) score
from Scores group by gender, day order by gender, day)a ,
(select @f_score:=0, @m_score:=0)b)c
二:核心在於把求累和的問題轉化爲求相同性別, 日期小於等於當前日期的記錄的分數之和,這邊用join,將表s1用gender和他之前的天數鏈接。
select * from Scores s1 left join Scores s2 on s1.gender = s2.gender
and s1.day >= s2.day order by s1.gender, s1.day
select s1.gender, s1.day, sum(s2.score_points) total from Scores s1
left join Scores s2 on s1.gender = s2.gender and s1.day >= s2.day
group by s1.gender, s1.day order by s1.gender, s1.day
1440. 計算布爾表達式的值🔒
https://leetcode-cn.com/problems/evaluate-boolean-expression/
name 是該表主鍵.該表包含了存儲的變量及其對應的值.
(left_operand, operator, right_operand) 是該表主鍵。該表包含了需要計算的布爾表達式.operator 是枚舉類型, 取值於('<', '>', '=')。left_operand 和 right_operand 的值保證存在於 Variables 表單中。
寫一個 SQL 查詢, 以計算表 Expressions 中的布爾表達式。返回的結果表沒有順序要求。查詢結果格式如下例所示。
如上所示, 你需要通過使用 Variables 表來找到 Expressions 表中的每一個布爾表達式的值。
題解
一:兩次連接取得左操作數和右操作數,然後 根據運算符計算結果,注意result.value是 varchar類型,而運算符的運算結果是數之行的0和1,需要if()轉換
select left_operand, right_operand, b.value , operator, c.value ,
case when operator = "=" then if(b.value= c.value, "true", "false")
when operator = ">" then if(b.value> c.value,"true", "false")
else if(b.value < c.value, "true", "false") end value from Expressions
left join Variables b on left_operand = b.name
left join Variables c on right_operand = c.name;
select left_operand, operator, right_operand, res value from
(select left_operand, right_operand, b.value b_v, operator, c.value c_v,
case when operator = "=" then if(b.value= c.value, "true", "false")
when operator = ">" then if(b.value> c.value,"true", "false")
else if(b.value < c.value, "true", "false") end res from Expressions
left join Variables b on left_operand = b.name
left join Variables c on right_operand = c.name)d
608. 樹節點🔒
https://leetcode-cn.com/problems/tree-node/
給定一個表 tree
,id 是樹節點的編號, p_id 是它父節點的 id 。
樹中每個節點屬於以下三種類型之一:葉子:如果這個節點沒有任何孩子節點。根:如果這個節點是整棵樹的根,即沒有父節點。內部節點:如果這個節點既不是葉子節點也不是根節點。
寫一個查詢語句,輸出所有節點的編號和節點的類型,並將結果按照節點編號排序。上面樣例的結果爲:
解釋:節點 '1' 是根節點,因爲它的父節點是 NULL ,同時它有孩子節點 '2' 和 '3' 。節點 '2' 是內部節點,因爲它有父節點 '1' ,也有孩子節點 '4' 和 '5' 。節點 '3', '4' 和 '5' 都是葉子節點,因爲它們都有父節點同時沒有孩子節點。
樣例中樹的形態如下:
注意:如果樹中只有一個節點,你只需要輸出它的根屬性。
題解
一:union的使用,not in注意,裏面排除掉了null,若不排除,會出錯。
(select distinct p_id from tree where p_id is not null)
select id, "Inner" as Type from tree where id in
(select distinct p_id from tree) and p_id is not null
union
select id, "Root" as Type from tree where p_id is null
union
select id, "Leaf" as Type from tree where id not in
(select distinct p_id from tree where p_id is not null) and p_id is not null
order by id
1204. 最後一個能進入電梯的人
https://leetcode-cn.com/problems/last-person-to-fit-in-the-elevator/
person_id 是這個表的主鍵。該表展示了所有等待電梯的人的信息。表中 person_id 和 turn 列將包含從 1 到 n 的所有數字,其中 n 是表中的行數。
電梯最大載重量爲 1000。寫一條 SQL 查詢語句查找最後一個能進入電梯且不超過重量限制的 person_name 。題目確保隊列中第一位的人可以進入電梯 。查詢結果如下所示 :
爲了簡化,Queue 表按 trun 列由小到大排序。上例中 George Washington(id 5), John Adams(id 3) 和 Thomas Jefferson(id 6) 將可以進入電梯,因爲他們的體重和爲 250 + 350 + 400 = 1000。Thomas Jefferson(id 6) 是最後一個體重合適並進入電梯的人。
題解
一:笛卡爾積,參見1308的法二,先找出小於等於1000的最大的turn,對應的name就是結果,這邊題目中給出,是按turn依次上電梯的。
select person_name from Queue q
where q.turn =
(select max(turn) max_turn from
(select q1.turn from Queue q1
left join Queue q2
on q1.turn >= q2.turn
group by q1.turn
having sum(q2.weight) <= 1000)b)
二:自定義變量,用sum_weight來記錄截止目前的總重量,用cnt來記錄對應的turn,這邊注意一下,要先按照turn由小到達排序(即按上電梯的順序排序,這樣變量統計的纔會是該turn下的總重量。
select turn, weight from Queue order by turn
select @sum_weight:= @sum_weight + weight as total_weight,
@cnt:= turn as num from (select turn, weight from Queue order by turn) q,
(select @sum_weight:=0, @cnt:=0)b
where @sum_weight <= 1000
然後從上述結果中取total_weight小於等於1000的最大值對應的turn,用原始表join,即可。
select person_name from Queue q left join
(select max(num) turn from
(select @sum_weight:= @sum_weight + weight as total_weight,
@cnt:= turn as num from (select turn, weight from Queue order by turn) q,
(select @sum_weight:=0, @cnt:=0)b
where @sum_weight <= 1000)c
where total_weight <= 1000)d
on q.turn =d.turn where d.turn is not null
1364. 顧客的可信聯繫人數量🔒
https://leetcode-cn.com/problems/number-of-trusted-contacts-of-a-customer/
customer_id 是這張表的主鍵。此表的每一行包含了某在線商店顧客的姓名和電子郵件。
(user_id, contact_email) 是這張表的主鍵。此表的每一行表示編號爲 use_id 的顧客的某位聯繫人的姓名和電子郵件。此表包含每位顧客的聯繫人信息,但顧客的聯繫人不一定存在於顧客表中。
invoice_id 是這張表的主鍵。此表的每一行分別表示編號爲 use_id 的顧客擁有有一張編號爲 invoice_id、價格爲 price 的發票。
爲每張發票 invoice_id 編寫一個SQL查詢以查找以下內容:customer_name:與發票相關的顧客名稱。price:發票的價格。contacts_cnt:該顧客的聯繫人數量。trusted_contacts_cnt:可信聯繫人的數量:既是該顧客的聯繫人又是商店顧客的聯繫人數量(即:可信聯繫人的電子郵件存在於客戶表中)。將查詢的結果按照 invoice_id 排序。
查詢結果的格式如下例所示:
Alice 有三位聯繫人,其中兩位(Bob 和 John)是可信聯繫人。Bob 有兩位聯繫人, 他們中的任何一位都不是可信聯繫人。Alex 只有一位聯繫人(Alice),並是一位可信聯繫人。John 沒有任何聯繫人。
題解
一:
select invoice_id, a.customer_name, price, ifnull(contacts_cnt, 0) contacts_cnt,
ifnull(trusted_contacts_cnt, 0) trusted_contacts_cnt from
(select invoice_id, customer_name,c.customer_id, price
from Invoices i left join Customers c on c.customer_id = i.user_id)a
left join (select c.customer_id, count(contact_name) contacts_cnt
from Customers c left join Contacts on Contacts.user_id = c.customer_id
group by c.customer_id)b on a.customer_id = b.customer_id
left join (select c.customer_id, count(contact_name) trusted_contacts_cnt
from Customers c left join Contacts on Contacts.user_id = c.customer_id
where contact_email in (select email from Customers)
group by c.customer_id)c on a.customer_id = c.customer_id
order by invoice_id
二:
select * from invoices i join customers c1 on i.user_id=c1.customer_id
left join contacts ct on i.user_id=ct.user_id
left join customers c2 on ct.contact_email=c2.email
order by i.invoice_id
select i.invoice_id, c1.customer_name, i.price,
count(ct.contact_name) contacts_cnt ,
count(c2.customer_name) trusted_contacts_cnt
from invoices i join customers c1 on i.user_id=c1.customer_id
left join contacts ct on i.user_id=ct.user_id
left join customers c2 on ct.contact_email=c2.email
group by i.invoice_id,c1.customer_name,price
order by i.invoice_id