相信很多學習SQL的小伙伴都面臨這樣的困境,學習完書本上的SQL基礎知識后,一方面想測試下自己的水平;另一方面想進一步提升,卻不知道方法。
其實,對于技能型知識,我的觀點一貫都是:多練習、多實踐。正所謂實踐出真知,學完書本的知識,很多時候也只能做到知道,距離熟練的應用還差的很遠。
在咱們程序員圈子里,力扣(LeetCode)和牛客(nowcoder.com)是兩個公認比較好的實踐平臺。題庫比較多,還有不少大廠的筆試真題,特別適合找工作時擼一擼。當然,作為平時個人技術提升的練習題,也是非常不錯的。
最近一段時間,我會先從力扣(LeetCode)的SQL題刷起。當然,順序可能是隨機的,歡迎小伙伴們點題。
題目:1308. 不同性別每日分數總計
(通過次數9,381 | 提交次數12,628,通過率74.29%)
表: Scores+---------------+---------+
| Column Name | Type |
+---------------+---------+
| player_name | varchar |
| gender | varchar |
| day | date |
| score_points | int |
+---------------+---------+
(gender, day)是該表的主鍵
一場比賽是在女隊和男隊之間舉行的
該表的每一行表示一個名叫 (player_name) 性別為 (gender) 的參賽者在某一天獲得了 (score_points) 的分數
如果參賽者是女性,那么 gender 列為 'F',如果參賽者是男性,那么 gender 列為 'M'寫一條SQL語句查詢每種性別在每一天的總分。
返回按gender和day對查詢結果 升序排序的結果。
查詢結果格式的示例如下。示例 1:
輸入:
Scores表:
+-------------+--------+------------+--------------+
| player_name | gender | day | score_points |
+-------------+--------+------------+--------------+
| Aron | F | 2020-01-01 | 17 |
| Alice | F | 2020-01-07 | 23 |
| Bajrang | M | 2020-01-07 | 7 |
| Khali | M | 2019-12-25 | 11 |
| Slaman | M | 2019-12-30 | 13 |
| Joe | M | 2019-12-31 | 3 |
| Jose | M | 2019-12-18 | 2 |
| Priya | F | 2019-12-31 | 23 |
| Priyanka | F | 2019-12-30 | 17 |
+-------------+--------+------------+--------------+
輸出:
+--------+------------+-------+
| gender | day | total |
+--------+------------+-------+
| F | 2019-12-30 | 17 |
| F | 2019-12-31 | 40 |
| F | 2020-01-01 | 57 |
| F | 2020-01-07 | 80 |
| M | 2019-12-18 | 2 |
| M | 2019-12-25 | 13 |
| M | 2019-12-30 | 26 |
| M | 2019-12-31 | 29 |
| M | 2020-01-07 | 36 |
+--------+------------+-------+
解釋:
女性隊伍:
第一天是 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 分來源:力扣(LeetCode)
鏈接:https://leetcode.cn/problems/running-total-for-different-genders
#測試數據
Create table If Not Exists Scores (player_name varchar(20), gender varchar(1), day date, score_points int);insert into Scores (player_name, gender, day, score_points) values ('Aron', 'F', '2020-01-01', '17');
insert into Scores (player_name, gender, day, score_points) values ('Alice', 'F', '2020-01-07', '23');
insert into Scores (player_name, gender, day, score_points) values ('Bajrang', 'M', '2020-01-07', '7');
insert into Scores (player_name, gender, day, score_points) values ('Khali', 'M', '2019-12-25', '11');
insert into Scores (player_name, gender, day, score_points) values ('Slaman', 'M', '2019-12-30', '13');
insert into Scores (player_name, gender, day, score_points) values ('Joe', 'M', '2019-12-31', '3');
insert into Scores (player_name, gender, day, score_points) values ('Jose', 'M', '2019-12-18', '2');
insert into Scores (player_name, gender, day, score_points) values ('Priya', 'F', '2019-12-31', '23');
insert into Scores (player_name, gender, day, score_points) values ('Priyanka', 'F', '2019-12-30', '17');
解題思路:
這道題在題目上其實有些描述不太準確的地方。
首先,題目里描述scores表的主鍵是(gender, day),但實際上,表里還有一個player_name的字段,難道每天同一個性別只有一個player(當然題目中給出的樣例數據確實是這樣,雖然跟實際情況可能不太相符)?
所以,相對來說,scores表的主鍵是(player_name, day)會比較合理一些。而gender僅僅是player_name的一個屬性而已。
其次,題目要求計算“每種性別在每一天的總分”。根據題目下面的解釋,可以知道,這個“總分”,其實是從最開始日期累計到當天的總分,而不僅僅是當天的分數加總。
基于以上兩點,我們再來看這道題。
源表scores的主鍵是(player_name, day),結果數據的主鍵是(gender, day)。而同一個gender下可能會存在多個player_name,所以,在計算上,需要做一次group by匯總操作。
簡單來看,如果僅僅是計算每天每個性別的總分,那直接使用下面的SQL語句就可以了。
selectgender,day,sum(score_points) as total
from scores
group by gender,day
order by gender,day;
但題目實際上要求的是計算累計值,即:最開始的那一天到現在的累計總分。
關于累計的計算,在SQL編寫的過程中,其實是一個比較經典,也比較常見的需求。常見于各類分析報表中。
思路上,一般來說,都是先構造出一個累加到每天的明細數據,然后再分組匯總即可。
具體到這道題,上面的SQL已經計算出了每天每個gender的匯總值,那么再將每一天的數據發散到所有比它大的日期上,最后再以gender+day分組匯總即可。
參考SQL:
with
total_score as (selectgender,day,sum(score_points) as totalfrom scoresgroup by gender,dayorder by gender,day
)
selecta.gender,a.day,sum(b.total) as total
from total_score a
inner join total_score b
on a.gender = b.gender
and a.day >= b.day
group by a.gender,a.day
order by a.gender,a.day;