一文搞懂 SQL:基礎知識和業務實踐總結

SQL的全稱是Structured Query Language(結構化查詢語言),是一種古老而簡潔的程序設計語言。看似平平無奇,一直被各種吐槽,但卻有著眾多語言所難得的漫長壽命,並展現出極好的拓展性,在不同時期衍生出不同的子語言。筆者作為騰訊TDW體系下的SQL現役運動員,對日常工作中常用的基礎知識和展開的業務實踐予以了總結,可供讀者參考。

結構化查詢語言,顧名思義,它的基礎在於結構化的數據庫表,最主要的應用場景在於數據查詢,雖然SQL也可以像其它語言一樣有一些高級的寫法,但它的主戰場並不在此,仍要回歸到對數據庫表的操作和處理中。以下分為基礎知識篇和業務實踐篇展開介紹,其中基礎知識篇盤點了一些常用的技能點,業務實踐篇則總結了幾點日常工作裡的思考。

第一部分 基礎知識篇

圍繞著數據庫表,可以展開許多的主題工作,有些是比較專業性的領域,如事務處理和權限管控等,這些更多是面向底層的技術基礎,部分屬於DBA的工作範疇。對於使用SQL的很大部分用戶群體來說,則集中於對數據庫表的增刪查改,聚合彙總裡,這些是面向業務的數據工作。針對這一塊的內容,繼續將其細分到不同的子場景裡,逐一展開介紹。

1.庫表基本操作

庫表預覽: SQL最基礎和最核心的兩個對象,便是數據庫和數據表,基於一個業務場景,可以有N個數據庫,在一個數據庫裡面,又可以有N張數據表。

數據庫的連接與切換,數據表的創建與刪除,是使用SQL進行庫表預覽的基本操作。 這些基本操作,可以通過前端的可視化界面進行,也可以從後臺直連數據庫展開,需由使用者所擁有的權限級別來選擇。

數據增刪: 除了一些常規的每日運行的計算任務外,很多時候我們只是單純地想對一張表進行處理,比如插入幾條數據,更新某個字段值,或者剔除幾條數據。這些操作往往是單次的,局部的,目的清晰,所以掌握幾個關鍵字就可以實現,如INSERT/UPDATE/DELETE等。

視圖應用: 視圖的引入,相當於在統計邏輯和實際庫表之間提供了一種折中的方案。完成這個功能,邏輯上是必須有這麼幾道工序的,但又不想在每一道工序裡都落地一張實際的數據表,顯得繁瑣而臃腫,那就引入視圖吧,把這些中間的工序用視圖的形式去實現和替代

關鍵字: 其實SQL真的是一門很簡潔的語言,市面上也不會有大本的書籍專門講述SQL的書寫方式,因為相對於其它語言來說,SQL歸根到底,只是圍繞著幾個關鍵字的一些基礎語句而已。

只要把這幾個關鍵字掌握了,SQL的大部分內容其實就已經展開了。

一文搞懂 SQL:基礎知識和業務實踐總結

2.數據查詢語句

SQL作為面向數據庫表的基礎語言,用戶群體具有多樣性,從技術底層往業務層走,往往會有DBA,數據開發,數據分析,產品經理等這些用戶角色。不同用戶群體對SQL的側重點是有差異的,但無論是哪一個群體,基本都繞不開數據查詢語句,是一塊必要內容。

簡單查詢: 能寫一個簡單查詢語句,其實就已展開了和數據庫表的對話過程。不管是哪種SQL的拓展語言,簡單查詢裡的語法基本都還是一致的。比如用*代表全量查詢,用distinct去重,用top和limit對數據條數做基本限制,以及用as對原表字段名進行替換更新等。

過濾查詢:在簡單查詢的基礎上,添加一些約束條件,也就是過濾查詢。比如你可以用關鍵字where查看其中某天的數據,用between或者in來限制一個範圍,用like或者relike來做正則匹配,也可以用and或者or這兩個通配符對這些約束條件進行排列組合。

排序查詢: 排序查詢可以細分為兩個場景,一個是在查詢內部的排序,即根據某個字段的屬性值進行表內部分區,對分區進行排序查詢後輸出,可以用row_number的形式來實現;另一個是把整個查詢當做一個整體,對結果表進行排序查詢後輸出,用order by來實現即可。

3.數據聚合與連接

前面講數據查詢語句,不管怎麼查詢,其實並不影響原生的表結構,即原來的表是按照什麼邏輯寫的數據,查詢結果裡的數據也是基於這種邏輯,只是篩選了局部數據而已。但數據聚合與連接就不一樣了,聚合會在縱向上改變原生表結構,連接則在橫向上拓展了表結構。

數據聚合: 要對一張表做數據聚合,其實理解了兩個概念即可,維度和指標。維度是你要基於哪些字段來做聚合,指標是在這個維度之上,你想用什麼彙總函數生成哪些指標。數據聚合的關鍵字是group by,維度裡的屬性值仍來自於原生表,指標則是新生成的彙總值。

數據連接: 對兩張表或者N張表做連接,是SQL裡面非常重要的一個內容,也是最容易埋坑的一個坑點。儘管數據連接只涉及四種方式,七個語法,但其仍然是絕大部分SQL腳本的核心內容。選擇合適的可靠的數據連接方式,應該是一個SQL運動員的基本功了。

一文搞懂 SQL:基礎知識和業務實踐總結

4.函數應用

函數庫,其實就像是一個數據處理與分析的百寶箱,收藏著各種場景下需要用到的車輪子。對函數庫的熟悉和掌握,可以較好地提升工作效率,也讓計算腳本顯得輕量而簡潔。畢竟站在通用的函數的肩膀上,很多統計邏輯是可以一步到位的,不需要沉迷於山重水複的自主構造裡。以下參考TDW的函數庫分類,將日常所用的函數細分為幾個子類別。

4.1數學函數: SQL裡的數學函數主要和數值處理有關,有取值函數和變換函數等。取值函數包括round四捨五入,abs取絕對值,ceil向上取整等,主要用於對具體數值的細節調整;變換函數則會改變該字段的數據分佈形態,如正弦sin,餘弦cos,或者開根號sqrt等。

4.2聚合函數: 在數據聚合中,選擇了具體字段作為聚合維度後,之後便是應用各種聚合函數得到彙總值的過程。其中有簡單聚合函數如count計數,sum求和,avg求平均,也可以基於分佈特徵,max/min取極值,std取標準差,variance取方差,另外若在聚合過程中涉及分區處理的話,也有rank,first/last_value,row_number等函數可以應用。

4.3時間和日期函數: 對時間數據的處理,同樣也是SQL裡的一個重要課題,主要細分為時間的加減,取值和轉換這麼三類。其中時間加減裡,又涉及不同的時間維度,比如按日維度有date_diff,date_add,date_sub等,按月維度有month_between,add_months等。時間取值函數則是在一個詳細的時間戳裡,取出自己想要的部分,如year,month,day,hour等。時間轉換函數則是時間形式的切換,如日期格式,格林尼治時間戳格式等。

4.4文本處理: 數據類型可以粗糙地分為數值數據和文本數據,對於文本數據的處理,也有很多對應的函數。其中有一些簡單取值函數,如通過length和size獲得字段長度和數組大小,通過upper和lower可以切換大小寫;字符串的切割與拼接,由淺入深有split,substr,concat,wm_concat等;最後正則表達式也是文本處理中一個特別重要的模塊。

4.5其它函數: 除了以上所盤點的一些通用函數外,其實在日常工作中會有很多垂直的業務場景,在這些特定場景下也有一些特定的函數邏輯。比如涉及數組結構拆分與重構時,可以應用later view函數;涉及字段編碼時,也有加密與解析函數;除此之外,還有邏輯函數,轉換函數等多種特殊函數,在特定的場景下,這些函數其實也是必要的。

5.具體開發環境的注意點

和其它眾多語言一樣,SQL的編寫也不能脫離其自身的開發環境,不同的數據庫形態,不同的IDE,都會有一些差異點和新特性。同樣的一段腳本,在A環境下可能跑的疾速如飛,在B環境下卻可能滿屏報錯,可以拿出來討論的,往往只是一些通用的邏輯和思考。除此之外,具體開發環境裡的注意事項,一些細節的加速點,則是要在具體環境裡去發現和探索。

第二部分 業務實踐篇

一種語言,一個函數庫,就像是廚房裡的各種廚具,它們可能或優良或劣質,但本質上還是一些標準化的組件。基於這些公共的組件,如何去烹製自己的美食,以及在烹製過程中的心得和思考,則屬於業務實踐的篇章。在業務實踐的過程中,不管怎樣去規範化,標準化,每個人輸出的腳本內容,難免還是要帶上私人的特質的,這同樣也是一個需要修習的部分。

以下通過三個問題點,來引出筆者在實際工作中的一些反思。其中如何儘量地少給未來挖坑,介紹了一些反面的案例,這些反面的細節在積累之後,容易引起整個系統的不穩定性;如何健康地做數據規劃,則是從一個創建者的身份,展開幾點數據規劃的思考;但不管做了多麼縝密和豐富的準備,隨著時間推移,總還是會出現變化的,所以在破舊與立新之間要找到平衡點。這些反思是基於工作實踐的層面,難免會有幼稚和紕漏之處,還請讀者輕拍。

1.如何儘量地少給未來挖坑

一文搞懂 SQL:基礎知識和業務實踐總結

不要起一些奇奇怪怪的名字: SQL裡的數據庫表,就像是其它語言裡的對象,往往是數量極大的,並在時間的推進,業務的發展中,基數會持續放大。所以對於數據庫名,數據表名,字段名,尤其是一些主鍵,索引的命名,務必要有一套相對統一的規範。缺乏規範的約束時,你無法想象人的想象力會多麼發散,這些奇奇怪怪的名字,最終會把人深深地困住的。

不要並行維護多個版本的數據: 因為業務的拓展,數據背後的口徑可能有所變更,基於舊有的數據報表,簡單修改後出一份新數據,是一種成本較低的實現方式。但最好不要並行維護多個版本的數據,當版本超過3個的時候,維護的成本是直線拉昇的。所以當要做數據變更時,一方面可以降低變更的頻率,另一方面儘量在原有報表裡修改,並替換掉原有口徑。

不要在單個腳本里寫過多內容: 統計邏輯的實現,就像是傳統工業裡的不同工序,這個過程裡存在兩種極端。一種是把一個邏輯在橫向/縱向細分為太多的工序,部署過多計算任務,形成很大冗餘;另一種是完全打包在一個大腳本里,這種情況也不利於問題定位和中間數據處理。所以不要在單個腳本里寫過多內容,可以將它拆分進最優數量的計算任務中。

要有一些基本的約束條件: 做一些事情時,不僅要立足於眼下的問題點,也要考慮一下未來可能發生的變化。就像是造一座橋,修一條路,總要考慮極限情況下的壓力。很多的數據異常,往往是在業務變化時,舊有的邏輯不能適應當前的場景。所以在一開始寫腳本時,要考慮一下未來的場景,

有一些基本的約束條件,這樣會讓所部署的任務會有較好的穩定性。

要採用儘量簡潔的寫法: 能夠一步到位的統計邏輯,就採用儘量簡潔的寫法,千萬不要去繞圈子。尤其是一些核心腳本,是要在不同環節,不同階段的同事之間傳承的,很多人並不瞭解當時的業務背景和需求邏輯,如果寫法太繞圈子的話,最終就把大家一起繞進去了。

2.如何健康地做數據規劃

數據規劃是一個層級比較中等的概念,往下一層,做需求開發時,往往只聚焦於特定的需求點,並不涉及其它內容;往上一層,做數據工程的話,又是基於整個部門,整個產品形態的框架搭建。但數據規劃更多是應對一個相對獨立的業務場景,所做的規劃與設計。

一個不夠好的數據規劃,可能會引發後續的諸多問題點,比如:

痛點1: PM提出要在視圖上擴展一個細分字段,覺得很簡單。我也覺得很簡單,但就是更改不了,因為這個字段在數據源處理中就捨棄了,無法從上一層數據表中獲得。

痛點2: 想要重跑一個時間範圍內的數據,但這張表不是分區表,無法並行處理;想要剔除某個日期內的數據,但不同表中時間格式不一致,導致處理結果有差漏等。

痛點3: 同樣的一條統計鏈路,部分為了保障每日推送而獨立出去,部分為了特性統計而獨立出去,由此產生了眾多的細分鏈路,此後的變更也要在不同鏈路之間同步處理。

以上列出的三個痛點,分別對應了原始信息的保留,技術實現的最優路徑,以及計算任務的細分問題等,不過也只是數據規劃需要思考的其中一部分問題點。在不同的業務場景下里,可以有不同的數據規劃思路。粗糙地講,可以分為數據基礎層和業務細分層獨立處理。

數據基礎層: 做一個數據規劃,首先應該要考慮數據本身,在數據基礎層裡,應暫時拋開具體的業務細節,以數據為重。這個時候應在處理中儘量地保留原始信息,同時要對數據源做好質量檢驗,第一道防線,往往是很重要的一道防線。

原始信息要完整,數據質量要合格,任務部署要輕便,這些是數據基礎層的一些目標,也是後續工作的一個前提。

業務細分層: 數據基礎要獨立而完整,面向數據本身;業務細分層則可以去細分實現,面向業務細節。基於不同的業務目標,可以從源表中篩選不同的內容,用於應對特定的場景。這樣的數據+業務層級,形成了一種“總-分”結構,是數據規劃的其中一種實現方式。

3.如何在破舊與立新之間尋找平衡點

很多的工作,都是基於當下的場景,即使做了詳盡的規劃和思考,也不可能應對未來的所有問題。當業務逐漸地深入發展時,很多的內容也需要做一些同步處理,小的層面上是一些數據表和視圖的表更,大的層面上可能涉及計算平臺的遷移,視圖系統的重建等。

破舊與立新,往往是一個長期存在的問題點。不需要每天都進行自我“革命”,但改良和優化,則是一個長期過程。在這個長期過程裡,我們需要在破舊與立新之間尋找到平衡點。

以上是筆者在TDW體系下寫SQL的一些實踐和思考,歡迎評論區留言討論~

作者:cooperyjli,騰訊 CDG 數據分析師


分享到:


相關文章: