千萬(wàn)別點(diǎn)開(kāi)!SQL詳細(xì)的「取數(shù)拆解」和「提速方案」來(lái)了!

唐亦六安?| 作者
知乎?|?來(lái)源https://zhuanlan.zhihu.com/p/113239595
剛接觸sql那會(huì),我總是遇到很多問(wèn)題,寫(xiě)的sql太過(guò)于冗雜或無(wú)從下手;連接邏輯不太清晰;解讀需求時(shí)間過(guò)長(zhǎng)等等。
一個(gè)SQL能夠解決的事情,我得整個(gè)4,5個(gè)小SQL挨個(gè)跑,效率太慢了。適應(yīng)一段時(shí)間后,發(fā)現(xiàn)SQL取數(shù)上還是有機(jī)可循,遂寫(xiě)一篇文章以便復(fù)盤(pán)。
1. 拆分取數(shù)(拆成整體和重要部分)
舉一個(gè)例子,假設(shè)水果店老板娘有個(gè)數(shù)據(jù)庫(kù)專(zhuān)門(mén)記錄一些銷(xiāo)售數(shù)據(jù),她的店里主要賣(mài)蘋(píng)果,橘子,西瓜,草莓,荔枝,葡萄。
有一天,水果店老板娘想問(wèn),這個(gè)月店里那些新顧客的情況,那判定條件其實(shí)很簡(jiǎn)單,就只需要選擇這個(gè)月,有第一次消費(fèi)記錄的顧客的信息抽出來(lái)即可。
而倘若問(wèn)這個(gè)月老顧客的消費(fèi)情況(老顧客指消費(fèi)有兩次及以上的顧客),這個(gè)時(shí)候取數(shù)如果直接設(shè)立條件為老顧客會(huì)很麻煩,判定條件不太好確定,這里可以直接寫(xiě)一個(gè)整體的SQL加上新顧客的SQL情況,再根據(jù)具體需求做相減即可。
當(dāng)然,這里不能直接相減,若直接相減,其實(shí)就是把新顧客和老顧客當(dāng)作了兩個(gè)獨(dú)立的圓,總的-新顧客=老顧客。這是不對(duì)的,因?yàn)?strong>會(huì)有重疊顧客情況,比如小A這個(gè)月第一次來(lái)到水果店,發(fā)現(xiàn)這個(gè)水果店的東西還是很好的,于是第二天又來(lái)購(gòu)買(mǎi)水果,對(duì)于小A而言,她是新顧客,又是老顧客的重疊用戶。
新顧客和老顧客實(shí)際是有交集的兩個(gè)圓,而非獨(dú)立的兩個(gè)圓,所以不能直接相減。這里我們唯一100%能確定的數(shù)據(jù)便是全部顧客的數(shù)據(jù)及新顧客的數(shù)據(jù)。老顧客的大體數(shù)據(jù)可以通過(guò)拆分為總的和新顧客的來(lái)得到。
2.?取數(shù)的分類(lèi)分組匯總問(wèn)題
取數(shù)時(shí)候我最怕碰到分類(lèi)向的問(wèn)題,這類(lèi)通常和group by這個(gè)函數(shù)緊密結(jié)合。當(dāng)聽(tīng)到分類(lèi)問(wèn)題的匯總計(jì)算時(shí)候,我腦子里是如下畫(huà)面:

然后會(huì)碰到的問(wèn)題是:
我該先取ABC還是abc還是q1-q18?
什么時(shí)候需要group by?
group by哪些變量,是全部還是一個(gè)?
需不需要去重?
怎么拼的呢?
尤其再加上,按頻率分組看看具體情況,就像腦子里有一個(gè)迷宮,有無(wú)數(shù)條路可以走,而且可以衍生非常多小路。于是,寫(xiě)著寫(xiě)著,我的眼神漸漸空洞起來(lái)......
好吧,其實(shí)實(shí)際上腦子里形成的是下面中間這個(gè)畫(huà)面的話,會(huì)變得清晰很多......
下圖,group by大寫(xiě)字母的話,它就像一個(gè)磁鐵一樣,A同一條數(shù)據(jù)和A同一條貼在一起但其他數(shù)據(jù)不相同的拼在一起,B和B,C和C,即大家都先各自找到組織聚在一起。
再然后,在大寫(xiě)字母這個(gè)大類(lèi)下,把小寫(xiě)字母的整一個(gè)小組,即group by小寫(xiě)字母。所以整個(gè)思路是group by 大寫(xiě)字母,小寫(xiě)字母(先寫(xiě)大分類(lèi),后寫(xiě)小分類(lèi)),確定完分類(lèi)之后,尤其一定要卡完最小的那個(gè)分類(lèi)之后,count(),avg(),sum()具體咋搞都行。

再通俗一點(diǎn)理解,group by像一個(gè)壓漢堡包機(jī)子,一條一條的數(shù)據(jù)就是肉片,只有同種的肉片都放在一塊(group by)之后,再匯總計(jì)算(最后整合)才能壓成一個(gè)漢堡。
簡(jiǎn)而言之,對(duì)于有涉及分類(lèi)的,而且是由大到小的,一定要select 大分類(lèi),小分類(lèi),再匯總計(jì)算類(lèi),最后group by部分的值要和計(jì)算匯總前面的那些分類(lèi)一一對(duì)應(yīng),如下:
想知道水果店不同時(shí)間(大分類(lèi))不同店面(小分類(lèi))的一些計(jì)算數(shù)據(jù):

又或者,想知道水果店不同時(shí)間(大分類(lèi))不同地區(qū)(中分類(lèi))不同店面(小分類(lèi))的一些計(jì)算數(shù)據(jù):

最后的一點(diǎn),可以有逆向思考方式,比如如果有兩個(gè)問(wèn)題,想知道不同時(shí)間不同地區(qū)的匯總值,以及不同時(shí)間不同地區(qū)不同店面的匯總值,那么只需要跑那個(gè)最詳細(xì)的sql——分類(lèi)到極致的那一個(gè)。
這里便是不同時(shí)間,不同地區(qū),不同店面的那個(gè)SQL。然后不同時(shí)間不同地區(qū)的那個(gè)匯總值,完全可以用上面那個(gè)SQL算出來(lái),在excel里面匯總整理即可,就不需要多跑了。
3. 頻次問(wèn)題
當(dāng)遇到,說(shuō)想知道每個(gè)顧客這個(gè)月來(lái)水果店的不同次數(shù)時(shí)候,我們需要新增一列,也就是頻率的,這里便涉及到子查詢。
這里先明確分類(lèi)標(biāo)準(zhǔn),即按照人出現(xiàn)的次數(shù)分一個(gè)類(lèi),那么為了把這個(gè)分類(lèi)弄出來(lái),先寫(xiě)個(gè)頻次的sql。
頻次標(biāo)準(zhǔn)是,只要我這個(gè)人在這個(gè)月只出現(xiàn)1次,那么我的頻次是1,如果出現(xiàn)了20次,那么我這個(gè)人的頻次就是20,以此類(lèi)推。(如果每天顧客最多出現(xiàn)一次,那么count(distinct?時(shí)間)。
寫(xiě)完子查詢中的頻次后,在外圍可以根據(jù)不同頻次下再去進(jìn)行深層次計(jì)算。

如果需求取數(shù)涉及到兩個(gè)表,b表沒(méi)有分類(lèi)的話,可以直接a表b表join一下

如果各自都涉及一個(gè)分類(lèi),需要各自取a,取b后整合成一個(gè)新的t表,從t中取數(shù)最后group by匯總。
注意:因?yàn)閠表合成了ab表的所有值,所以這里取的AB表中的所有值最后都要group by一下(對(duì)應(yīng)下面代碼的★)
合成為t表之后,直接t表取數(shù),匯總,最后在group by完成

4. 其他一些細(xì)節(jié)
1.時(shí)間上如果想取近幾天的數(shù)據(jù),除了用直接的>='2020-03-15'外,還可以用date_sub函數(shù),指的是從日期減去指定的時(shí)間間隔。
比如:date_sub(default. sysdate (-1),6)指的是距離此時(shí)近七天的時(shí)間。
2.連接兩個(gè)表的時(shí)候,union可以理解為上下拼表(所以兩個(gè)表的變量名一定要一致),join是左右拼表
3.關(guān)于left join,舉一個(gè)例子,現(xiàn)在有兩個(gè)表,A表是顧客信息表,B表是商品表(兩個(gè)表都有”顧客“這個(gè)變量名),現(xiàn)在B表中“水果名字"變量下的枚舉值有蘋(píng)果,橘子,荔枝等等。
現(xiàn)在想取的是,吃了蘋(píng)果和橘子的那些顧客的具體數(shù)據(jù)值。這里涉及到去重,當(dāng)時(shí)我在想,A表中的一個(gè)顧客,如果既吃了蘋(píng)果,又吃了橘子,那么他就會(huì)對(duì)應(yīng)B表中的兩條數(shù)據(jù)了,如何去重呢?
left join便是適用在這種情況下,A left join B,只會(huì)是全滿足A表下再去匹配表,對(duì)于那些A表中沒(méi)有,B表有的,便會(huì)是null代替了。最后再count(distinct b.顧客)便可以把去重后的顧客數(shù)給算出來(lái)了。
以上都是很淺顯的sql理解,其他的想到再補(bǔ)充,希望自己之后取數(shù)能夠快一點(diǎn)!
-?END -
本文為轉(zhuǎn)載分享&推薦閱讀,若侵權(quán)請(qǐng)聯(lián)系后臺(tái)刪除