面試官提問:如何通過SQL方式將數(shù)據(jù)庫表行轉(zhuǎn)列?
本文轉(zhuǎn)載自微信公眾號(hào)「Java極客技術(shù)」,作者鴨血粉絲Tang 。轉(zhuǎn)載本文請(qǐng)聯(lián)系Java極客技術(shù)公眾號(hào)。
一、提問環(huán)節(jié)
在剛進(jìn)入 IT 行業(yè)的第一年換工作的時(shí)候,至今讓我印象最深刻的有一個(gè)這樣的面試題:如何通過 SQL 方式將數(shù)據(jù)庫的行轉(zhuǎn)列?
當(dāng)時(shí)的面試官讓我現(xiàn)場寫 SQL,信心滿滿的我,我覺得我可以做出來,然后10分支、20分鐘、30分鐘...過去了,很遺憾一點(diǎn)動(dòng)靜都沒有。
最后的我不得不服,結(jié)局相信大家也能猜到是啥了!??
二、場景分析
面試結(jié)束之后,不服輸?shù)奈覜Q定要把這個(gè)問題給破解掉,回到自己的租處之后,打開電腦,決定從0開始琢磨,怎么實(shí)現(xiàn)行轉(zhuǎn)列呢?
其實(shí)如果你是一個(gè)經(jīng)常玩 sql 的人,相信看到這個(gè)提問的時(shí)候,你心里已經(jīng)有答案了,解決這個(gè)問題,方法其實(shí)很簡單,通過下面這個(gè)語法即可實(shí)現(xiàn)。
- case when ... then ... else ... end
例如下面是一張很常見的學(xué)生考試成績表,我們將學(xué)生的考試成績以單表的形式存儲(chǔ)到數(shù)據(jù)庫表中。
我們想要以下圖形式,并以總分排名從高到底進(jìn)行展示,如何通過 SQL 方式實(shí)現(xiàn)呢?
有的同學(xué)說,我可以通代碼層面來實(shí)現(xiàn),不可否認(rèn),代碼完全可以實(shí)現(xiàn),只需要封裝一個(gè)如下形式的數(shù)據(jù)結(jié)構(gòu)就可以了。
- //學(xué)生姓名為key,相同key的數(shù)據(jù)封裝到List集合中
- Map<String, List<StudentExam> studentExamMap = new HashMap();
其中學(xué)生姓名就是一個(gè)Key,然后把相同學(xué)生姓名的數(shù)據(jù)封裝到List
在面對(duì)少量數(shù)據(jù)的時(shí)候,這種方式?jīng)]問題,只是計(jì)算復(fù)雜了一點(diǎn),但是當(dāng)數(shù)據(jù)庫表超過 5000 以上的時(shí)候,這種在代碼層面的計(jì)算,內(nèi)存就有點(diǎn)吃不消了,因此極其不推薦采用。
面對(duì)這種場景需求,我們多半會(huì)采用通過 sql 方式來解決,那么通過 sql 方式破解呢?請(qǐng)看下圖
其中最關(guān)鍵的一步就是先用case when ... then ... else ... end語法將不同的課程分?jǐn)?shù)分離出來,然后通過sum + group聚合函數(shù)查詢進(jìn)行分?jǐn)?shù)匯總,最后通過order by語法將分?jǐn)?shù)進(jìn)行從高到低排序,進(jìn)而達(dá)到我們想要的預(yù)期效果!
其實(shí)像這樣的行轉(zhuǎn)列的查詢邏輯非常的普遍,例如剛過去的奧運(yùn)獎(jiǎng)牌排行榜!
還有全球新冠疫情數(shù)據(jù)排名。
可能不同的應(yīng)用實(shí)現(xiàn)方式不一樣,但是大體的解決思路是一樣的,將數(shù)據(jù)進(jìn)行分組聚合匯總,然后按照分?jǐn)?shù)進(jìn)行從高到低排名。
通過 SQL 實(shí)現(xiàn)還有一個(gè)非常大的好處,就是可以根據(jù)不同的維度進(jìn)行排序,同時(shí)支持多個(gè)字段進(jìn)行排序,如果在代碼層面去實(shí)現(xiàn)排序,相當(dāng)復(fù)雜。
三、小結(jié)
本文主要圍繞如何通過 sql 的方式,將數(shù)據(jù)庫表中的行轉(zhuǎn)列進(jìn)行顯示,希望能幫助到大家!