Leetcode SQL(四)

 

目錄

612. 平面上的最近距離🔒

1355. 活動參與者🔒

1164. 指定日期的產品價格🔒

1193. 每月交易 I🔒

602. 好友申請 II :誰有最多的好友🔒

1112. 每位學生的最高成績🔒

1264. 頁面推薦🔒

178. 分數排名

585. 2016年的投資🔒

1341. 電影評分🔒


612. 平面上的最近距離🔒

https://leetcode-cn.com/problems/shortest-distance-in-a-plane/

表 point_2d 保存了所有點(多於 2 個點)的座標 (x,y) ,這些點在平面上兩兩不重合。寫一個查詢語句找到兩點之間的最近距離,保留 2 位小數。

最近距離在點 (-1,-1) 和(-1,2) 之間,距離爲 1.00 。所以輸出應該爲:

注意:任意點之間的最遠距離小於 10000 。

題解

一:兩張表join,笛卡爾積,會產生n^ 2個兩兩組合對,同時沒法排除自身和自身的組合。不過好在題中交代這些點在平面上兩兩不重合,所以兩兩組合x和y不同時相等可以排除自身組合。這邊用row_number函數給每一條記錄生成一個id,join時用a的id join比b的id大的,可排除自己join,就算有重疊點也不影響,因爲重疊點id畢竟不同,而且只有(n^2 - n) / 2個兩兩組合對。

select a.*, b.*
from (select row_number() over() as id, x, y from point_2d) a 
join (select row_number() over() as id, x, y from point_2d) b
on a.id > b.id

select round(min(sqrt(POW(a.x - b.x, 2) + POW(a.y - b.y, 2))), 2) as shortest
from (select row_number() over() as id, x, y from point_2d) a 
join (select row_number() over() as id, x, y from point_2d) b
on a.id > b.id

1355. 活動參與者🔒

https://leetcode-cn.com/problems/activity-participants/

寫一條 SQL 查詢那些既沒有最多,也沒有最少參與者的活動的名字可以以任何順序返回結果,Activities 表的每項活動的參與者都來自 Friends 表。下面是查詢結果格式的例子:

題解

一:some函數的使用

SELECT activity FROM Friends GROUP BY activity HAVING
COUNT(Friends.id) > SOME(SELECT count(id) cnt FROM Friends GROUP BY activity)
AND COUNT(Friends.id) < SOME(SELECT count(id) cnt FROM Friends GROUP BY activity)
SELECT activity FROM Friends GROUP BY activity HAVING
COUNT(Friends.id) != 
(SELECT MIN(cnt) FROM (SELECT count(id) cnt FROM Friends GROUP BY activity)a)
AND COUNT(Friends.id) != 
(SELECT MAX(cnt) FROM (SELECT count(id) cnt FROM Friends GROUP BY activity)b)

1164. 指定日期的產品價格🔒

https://leetcode-cn.com/problems/product-price-at-a-given-date/

這張表的主鍵是 (product_id, change_date)。這張表的每一行分別記錄了 某產品 在某個日期 更改後 的新價格。
寫一段 SQL來查找在 2019-08-16 時全部產品的價格,假設所有產品在修改前的價格都是 10。查詢結果格式如下例所示:

題解

一:

select distinct p.product_id, ifnull(b.price, 10) price
from Products p left join
(
select a.product_id, new_price price from
	(
	select product_id, max(change_date) change_date from Products 
	where  change_date <= '2019-08-16'
	group by product_id
	)a
left join Products p 
on p.product_id = a.product_id and a.change_date = p.change_date
)b
on p.product_id = b.product_id

b表:查詢的是 2019-08-16之前的有過修改的價格,即有過修改的最終價格,那麼還有商品未被修改過,他們應該是10,這一部分是通過原來的表join來取到未修改的id。時間效率不高

二:union ,法一中b表只取到了修改過的最新價格。法二換一種思路,若是有過修改的取最後一次修改日期;若是未經修改的取第一次修改日期(必定大於2019-08-16)即表a所做的事,如下圖所示。然後用原來的表join,join的條件取相同的id,相同的修改日期,則可依據修改日期分情況取price。時間效率還不錯。

select p.product_id, case 
when a.change_date > '2019-08-16' then 10
else p.new_price end price 
from Products p,
(
select product_id, max(change_date) change_date from Products 
where  change_date <= '2019-08-16'
group by product_id
union
select product_id, min(change_date) change_date from products
group by product_id having min(change_date) > '2019-08-16'
)a
where p.product_id = a.product_id and p.change_date = a.change_date

1193. 每月交易 I🔒

https://leetcode-cn.com/problems/monthly-transactions-i/

id 是這個表的主鍵。該表包含有關傳入事務的信息。state 列類型爲 “[”批准“,”拒絕“] 之一。
編寫一個 sql 查詢來查找每個月和每個國家/地區的事務數及其總金額、已批准的事務數及其總金額。查詢結果格式如下所示:

題解

一:字符串函數left(trans_date,7),從trans_date字段左邊起取7個字符。

select left(trans_date,7) month, country, count(state) 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_amount
from Transactions group by month, country

二:日期格式轉換函數DATE_FORMAT(trans_date, '%Y-%m')函數。

select DATE_FORMAT(trans_date, '%Y-%d') month from Transactions

select DATE_FORMAT(trans_date, '%Y-%m') month, country, count(state) 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_amount
from Transactions group by month, country

602. 好友申請 II :誰有最多的好友🔒

https://leetcode-cn.com/problems/friend-requests-ii-who-has-the-most-friends/

在 Facebook 或者 Twitter 這樣的社交應用中,人們經常會發好友申請也會收到其他人的好友申請。表 request_accepted 存儲了所有好友申請通過的數據記錄,其中, requester_id 和 accepter_id 都是用戶的編號。

寫一個查詢語句,求出誰擁有最多的好友和他擁有的好友數目。對於上面的樣例數據,結果爲:

注意:保證擁有最多好友數目的只有 1 個人。好友申請只會被接受一次,所以不會有 requester_id 和 accepter_id 值都相同的重複記錄。
解釋:編號爲 '3' 的人是編號爲 '1','2' 和 '4' 的好友,所以他總共有 3 個好友,比其他人都多。

進階:在真實世界裏,可能會有多個人擁有好友數相同且最多,你能找到所有這些人嗎?

題解

一:union 與union all的區別:union:對兩個結果集進行並集操作,不包括重複行,同時進行默認規則的排序;union All:對兩個結果集進行並集操作,包括重複行,不進行排序;這邊使用union all,因爲a中的兩張表可能會有重複的行,例如當accepter_id=requester_id,且計數也相同,則union會刪除相同行。

select id, sum(cnt) as num from
(
    select requester_id id, count(accepter_id) cnt from request_accepted group by requester_id
    union all
    select accepter_id id, count(requester_id) cnt from request_accepted group by accepter_id
)a
group by id 
order by num desc  limit 1
select requester_id id, count(accepter_id) cnt from request_accepted group by requester_id
union all
select accepter_id id, count(requester_id) cnt from request_accepted group by accepter_id
order by id

1112. 每位學生的最高成績🔒

https://leetcode-cn.com/problems/highest-grade-for-each-student/

(student_id, course_id) 是該表的主鍵。

編寫一個 SQL 查詢,查詢每位學生獲得的最高成績和它所對應的科目,若科目成績並列,取 course_id 最小的一門。查詢結果需按 student_id 增序進行排序。查詢結果格式如下所示:

Enrollments 表:

題解

一:先查出每個學生的id和最大成績然後聯源數據表。

select e.student_id, min(course_id) as course_id, grade from
Enrollments e,
(select student_id, max(grade) max_grad from Enrollments group by student_id)a 
where e.student_id = a.student_id and e.grade = a.max_grad
group by e.student_id, grade order by e.student_id

二: 用源數據表直接查

SELECT student_id,MIN(course_id) course_id,MAX(grade) grade FROM enrollments
WHERE (student_id,grade) IN (SELECT student_id,max(grade) FROM enrollments GROUP BY student_id)
GROUP BY student_id ORDER BY student_id ;

三:轉自leetcode上的題解,使用limit固定排列順序https://leetcode-cn.com/problems/highest-grade-for-each-student/solution/dui-pai-hao-xu-de-shu-ju-fen-zu-cha-xun-by-hua-g/

select t.student_id, t.course_id, t.grade
from
(
    select student_id, course_id, grade from Enrollments
    order by student_id asc, grade desc, course_id asc limit 1000
)t
group by t.student_id

1264. 頁面推薦🔒

https://leetcode-cn.com/problems/page-recommendations/

這張表的主鍵是 (user1_id, user2_id)。這張表的每一行代表着 user1_id 和 user2_id 之間存在着朋友關係。
喜歡列表: Likes

這張表的主鍵是 (user_id, page_id)。這張表的每一行代表着 user_id 喜歡 page_id。
寫一段 SQL  向user_id = 1 的用戶,推薦其朋友們喜歡的頁面。不要推薦該用戶已經喜歡的頁面。你返回的結果中不應當包含重複項。返回結果的格式如下例所示:

用戶1 同 用戶2, 3, 4, 6 是朋友關係。推薦頁面爲: 頁面23 來自於 用戶2, 頁面24 來自於 用戶3, 頁面56 來自於 用戶3 以及 頁面33 來自於 用戶6。頁面77 同時被 用戶2 和 用戶3 推薦。頁面88 沒有被推薦,因爲 用戶1 已經喜歡了它。

題解

一:union 和子查詢

select distinct(page_id) recommended_page from Likes 
where user_id in 
(select user1_id id from Friendship where user2_id = 1
union 
select user2_id id from Friendship where user1_id = 1)
and page_id not in (select page_id from Likes where user_id = 1)

二:UNION 思路,先找出1的所有朋友,即表a中所有的id。再join原表。

select distinct(page_id) recommended_page from Likes,
(select user1_id id from Friendship where user2_id = 1
union 
select user2_id id from Friendship where user1_id = 1)a
where user_id = id and page_id not in (select page_id from Likes where user_id = 1)

三:case when 來查詢出其朋友

select distinct(page_id) recommended_page from Likes,
(select case
when user1_id = 1 then user2_id
when user2_id = 1 then user1_id end id from Friendship
where user1_id = 1 or user2_id = 1
)a
where user_id = id and page_id not in (select page_id from Likes where user_id = 1)

178. 分數排名

https://leetcode-cn.com/problems/rank-scores/

編寫一個 SQL 查詢來實現分數排名。如果兩個分數相同,則兩個分數排名(Rank)相同。請注意,平分後的下一個名次應該是下一個連續的整數值。換句話說,名次之間不應該有“間隔”。

例如,根據上述給定的 Scores 表,你的查詢應該返回(按分數從高到低排列):

重要提示:對於 MySQL 解決方案,如果要轉義用作列名的保留字,可以在關鍵字之前和之後使用撇號。例如 `Rank`

題解

一:自定義變量

select Score, cast(num as UNSIGNED) 'Rank' from
(select Score, 
case when @sc = Score then @num := @num
else @num := @num + 1 end num, @sc :=Score from
(select Score from Scores order by Score desc)a,
(select @num := 0, @sc := null)b)c

二:不連續排名函數

select Score, dense_rank() over (order by Score desc)  as `Rank`
from Scores;

585. 2016年的投資🔒

https://leetcode-cn.com/problems/investments-in-2016/

寫一個查詢語句,將 2016 年 (TIV_2016) 所有成功投資的金額加起來,保留 2 位小數。對於一個投保人,他在 2016 年成功投資的條件是:他在 2015 年的投保額 (TIV_2015) 至少跟一個其他投保人在 2015 年的投保額相同。他所在的城市必須與其他投保人都不同(也就是說維度和經度不能跟其他任何一個投保人完全相同)。
輸入格式:
表 insurance 格式如下:

PID 字段是投保人的投保編號, TIV_2015 是該投保人在2015年的總投保金額, TIV_2016 是該投保人在2016年的投保金額, LAT 是投保人所在城市的維度, LON 是投保人所在城市的經度。

解釋:就如最後一個投保人,第一個投保人同時滿足兩個條件:1. 他在 2015 年的投保金額 TIV_2015 爲 '10' ,與第三個和第四個投保人在 2015 年的投保金額相同。2. 他所在城市的經緯度是獨一無二的。第二個投保人兩個條件都不滿足。他在 2015 年的投資 TIV_2015 與其他任何投保人都不相同。且他所在城市的經緯度與第三個投保人相同。基於同樣的原因,第三個投保人投資失敗。所以返回的結果是第一個投保人和最後一個投保人的 TIV_2016 之和,結果是 45 。

題解

一:第一個條件:通過選出TIV_2015中出現過不止一次的標出;第二個條件,通過分組來找出,同一個經緯的只有一個投保人的PID。

select sum(TIV_2016) TIV_2016 from insurance where PID in
(select PID from insurance group by LAT, LON having count(PID) = 1)
and TIV_2015 in (select TIV_2015 from insurance group by TIV_2015 having count(PID) >= 2)

二:這邊注意concat函數的使用,可以在比較中使用多個字段。

select sum(TIV_2016) TIV_2016 from insurance where concat(LAT, LON) 
in (SELECT concat(LAT, LON) from insurance group by LAT , LON having COUNT(PID) = 1)
and TIV_2015 in (select TIV_2015 from insurance group by TIV_2015 having count(PID) >= 2)

1341. 電影評分🔒

https://leetcode-cn.com/problems/movie-rating/

(movie_id, user_id) 是這個表的主鍵。這個表包含用戶在其評論中對電影的評分 rating 。created_at 是用戶的點評日期。 
請你編寫一組 SQL 查詢:查找評論電影數量最多的用戶名。如果出現平局,返回字典序較小的用戶名。查找在 2020 年 2 月 平均評分最高 的電影名稱。如果出現平局,返回字典序較小的電影名稱。查詢分兩行返回,查詢結果格式如下例所示:

Daniel 和 Monica 都點評了 3 部電影("Avengers", "Frozen 2" 和 "Joker") 但是 Daniel 字典序比較小。Frozen 2 和 Joker 在 2 月的評分都是 3.5,但是 Frozen 2 的字典序比較小。

題解

一:union all大法,注意要加括號

(select name as results from Movie_Rating r left join Users u 
on r.user_id = u.user_id group by r.user_id order by count(movie_id) desc, name limit 1)
union all
(select title as results from Movie_Rating r left join Movies m 
on r.movie_id = m.movie_id 
where created_at like "2020-02-%"
group by r.movie_id order by avg(rating) desc, title limit 1)

 

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