Leetcode SQL(三)

目錄

1393. 股票的資本損益🔒

1270. 向公司CEO彙報工作的所有人🔒

1398. 購買了產品A和產品B卻沒有購買產品C的顧客🔒

1285. 找到連續區間的開始和結束數字🔒

1308. 不同性別每日分數總計🔒

1440. 計算布爾表達式的值🔒

608. 樹節點🔒


 

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 排序。查詢結果格式如下面的例子:

題解

一:轉自https://leetcode-cn.com/problems/find-the-start-and-end-number-of-continuous-ranges/solution/jian-yi-fang-fa-zhao-dao-lian-xu-zhi-de-tou-he-wei/

思路:
從結果樣例來看,分別需要輸出連續數列的頭和尾。
那麼,我們需要加工並獲取一個連續數列的頭,以及其對應的數列(如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/

給定一個表 treeid 是樹節點的編號, 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

 

 

 

 

 

 

 

 

 

 

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章