本系列是 SQL 系列的開篇,介紹一些巨集觀與基礎的內容。
SQL 是什麼?
SQL 是一種結構化查詢語言,用於管理關係型資料庫,我們 90% 接觸的都是查詢語法,但其實它包含完整的增刪改查和事物處理功能。
宣告式特性
SQL 屬於宣告式程式語言,而現代通用程式語言一般都是命令式的。但是不要盲目崇拜宣告式語言,比如說它未來會代替低階的命令式語言,因為宣告式本身也有它的缺點,它與命令式語言也有相通的地方。
為什麼我們覺得宣告式程式語言更高階?因為宣告式語言抽象程度更高,比如 select * from table1
僅描述了要從 table1 查詢資料,但查詢的具體步驟的完全沒提,這背後可能存在複雜的索引優化與鎖機制,但我們都無需關心,這簡直是程式設計的最高境界。
那為什麼現在所有通用業務程式碼都是命令式呢?因為 命令式給了我們描述具體實現的機會 ,而通用領域的程式設計正需要建立在嚴謹的實現細節上。比如校驗使用者許可權這件事,即便 AI 程式設計提供了將 “登陸使用者僅能訪問有許可權的資源” 轉化為程式碼的能力,我們也不清楚資源具體指哪些,以及在許可權轉移過程中的資源所有權屬於誰。
SQL 之所以能保留宣告式特性,完全因為鎖定了關係型資料管理這個特定領域,而恰恰對這個領域的需求是標準化且可列舉的,才使宣告式成為可能。
基於命令式語言也完全可擴充出宣告式能力,比如許多 ORM 提供了類似 select({}).from({}).where({})
之類的語法,甚至一個 login()
函式也是宣告式程式設計的體現,因為呼叫者無需關心是如何登陸的,總之呼叫一下就完成了登陸,這不就是宣告式的全部精髓嗎?
語法分類
作為關係型資料庫管理工具,SQL 需要定義、操縱與控制資料。
資料定義即修改資料庫與表級別結構,這些是資料結構,或者是資料元資訊,它不代表具體資料,但描述資料的屬性。
資料操縱即修改一行行具體資料,增刪改查。
資料控制即對事務、使用者許可權的管理與控制。
資料定義
DDL(Data Definition Language)資料定義,包括 CREATE
DROP
ALTER
方法。
資料操縱
DML(Data Manipulation Language)資料操縱,包括 SELECT
INSERT
UPDATE
DELETE
方法。
資料控制
DCL(Data Control Language)資料控制,包括 COMMIT
、ROLLBACK
等。
所有 SQL 操作都圍繞這三種型別,其中資料操縱幾乎佔了 90% 的程式碼量,畢竟資料查詢的訴求遠大於寫,資料寫入對應資料採集,而資料查詢對應資料分析,資料分析領域能玩出的花樣遠比資料採集要多。
PS:有些情況下,會把最重要的 SELECT
提到 DQL(Data Query Language)分類下,這樣分類就變成了四個。
集合運算
SQL 世界的第一公民是集合,就像 JAVA 世界第一公民是物件。我們只有以集合的視角看待 SQL,才能更好的理解它。
何為集合視角,即所有的查詢、操作都是二維資料結構中進行的,而非小學算術裡的單個數字間加減乘除關係。
集合的運算一般有 UNION
並集、EXCEPT
差集、INTERSECT
交集,這些都是以行為單位的操作,而各種 JOIN 語句則是以列為單位的集合運算,也是後面提到的連線查詢。
只要站在二維資料結構中進行思考,運算無非是橫向或縱向的操作。
資料正規化
資料正規化分為五層,每層要求都比上一層更嚴苛,因此是一個可以逐步遵循的正規化。資料正規化要求資料越來越解耦,減少冗餘。
比如第一正規化要求每列都具有原子性,即都是不可分割的最小資料單元。如果資料採集時,某一列作為字串儲存,並且以 "|" 分割表示省市區,那麼它就不具有原子性。
當然實際生產過程往往不都遵循這種標準,因為表不是孤立的,在資料處理流中,可能在某個環節再把列原子化,而原始資料為了壓縮體積,進行列合併處理。
希望違反正規化的還不僅是底層表,現在大資料處理場景下,越來越多的業務採用大寬表結構,甚至故意進行資料冗餘以提升查詢效率,列儲存引擎就是針對這種場景設計的,所以資料正規化在大資料場景下是可以變通的,但依然值得學習。
聚合
當採用 GROUP BY 分組聚合資料時,如希望針對聚合值篩選,就不能用 WHERE 限定條件了,因為 WHERE 是基於行的篩選,而不是針對組合的。(GROUP BY 對資料進行分組,我們稱這些組為 “組合”),所以需要使用針對組合的篩選語句 HAVING:
SELECT SUM(pv) FROM table
GROUP BY city
HAVING AVG(uv) > 100
這個例子中,如果 HAVING 換成 WHERE 就沒有意義,因為 WHERE 加聚合條件時,需要對所有資料進行合併,不符合當前檢視的詳細級別。(關於檢視詳細級別,在我之前寫的 精讀《什麼是 LOD 表示式》 有詳細說明)。
聚合如此重要,是因為我們分析資料必須在高 LEVEL 視角看,明細資料是看不出趨勢的。而複雜的需求往往伴隨著帶有聚合的篩選條件,明白 SQL 是如何支援的非常重要。
CASE 表示式
CASE 表示式分為簡單與搜尋 CASE 表示式,簡單表示式:
SELECT CASE pv WHEN 1 THEN 'low' ELSE 'high' END AS quality
上面的例子利用 CASE 簡單表示式形成了一個新欄位,這種模式等於生成了業務自定義臨時欄位,在對當前表進行資料加工時非常有用。搜尋 CASE 表示式能力完全覆蓋簡單 CASE 表示式:
SELECT CASE WHEN pv < 100 THEN 'low' ELSE 'high' END AS quality
可以看到,搜尋 CASE 表示式可以用 “表示式” 描述條件,可以輕鬆完成更復雜的任務,甚至可以在表示式裡使用子查詢、聚合等手段,這些都是高手寫 SQL 的慣用技巧,所以 CASE 表示式非常值得深入學習。
複雜查詢
SELECT 是 SQL 最複雜的部分,其中就包含三種複雜查詢模式,分別是連線查詢與子查詢。
連線查詢
指 JOIN 查詢,比如 LEFT JOIN、RIGHT JOIN、INNER JOIN。
在介紹聚合時我們提到了,連線查詢本質上就是對列進行擴充,而兩個表之間不會無緣無故合成一個,所以必須有一個外來鍵作為關係紐帶:
SELECT A.pv, B.uv
FROM table1 as t1 LEFT JOIN table2 AS P t2
ON t1.productId = t2.productId
連線查詢不僅擴充了列,還會隨之擴充行,而擴充方式與連線的查詢的型別有關。除了連線查詢別的表,還可以連線查詢自己,比如:
SELECT t1.pv AS pv1, P2.pv AS pv2
FROM tt t1, tt t2
這種子連線查詢結果就是自己對自己的笛卡爾積,可通過 WHERE 篩選去重,後面會有文章專門介紹。
子查詢與檢視
子查詢就是 SELECT 裡套 SELECT,一般來說 SELECT 會從內到外執行,只有在關聯子查詢模式下,才會從外到內執行。
而如果把子查詢儲存下來,就是一個檢視,這個檢視並不是實體表,所以很靈活,且資料會隨著原始表資料而變化:
CREATE VIEW countryGDP (country, gdp)
AS
SELECT country, SUM(gdp)
FROM tt
GROUP BY country
之後 countryGDP
這個檢視就可以作為臨時表來用了。
這種模式其實有點違背 SQL 宣告式的特點,因為定義檢視類似於定義變數,如果繼續寫下去,勢必會形成一定命令式思維邏輯,但這是無法避免的。
事務
當 SQL 執行一連串操作時,難免遇到不執行完就會出現髒資料的問題,所以事務可以保證操作的原子性。一般來說每個 DML 操作都是一個內建事務,而 SQL 提供的 START TRANSACTION 就是讓我們可以自定義事務範圍,使一連串業務操作都可以包裝在一起,成為一個原子性操作。
對 SQL 來說,原子性操作是非常安全的,即失敗了不會留下任何痕跡,成功了會全部成功,不會存在中間態。
OLAP
OLAP(OnLine Analytical Processing)即實時資料分析,是 BI 工具背後計算引擎實現的基礎。
現在越來越多的 SQL 資料庫支援了視窗函式實現,用於實現業務上的 runningSum 或 runningAvg 等功能,這些都是資料分析中很常見的。
以 runningSum 為例,比如雙十一實時表的資料是以分鐘為單位的實時 GMV,而我們要做一張累計到當前時間的 GMV 彙總折線圖,Y 軸就需要支援 running_sum(GMV)
這樣的表示式,而這背後可能就是通過視窗函式實現的。
當然也不是所有業務函式都由 SQL 直接提供,業務層仍需實現大量記憶體函式,在 JAVA 層計算,這其中一部分是需要下推到 SQL 執行的,只有記憶體函式與下推函式結合在一起,才能形成我們在 BI 工具看到的複雜計算欄位效果。
總結
SQL 是一種宣告式語言,一個看似簡單的查詢語句,在引擎層往往對應著複雜的實現,這就是 SQL 為何如此重要卻又如此普及的原因。
雖然 SQL 容易上手,但要系統的理解它,還得從結構化資料與集合的概念開始進行思想轉變。
不要小看 CASE 語法,它不僅與容易與程式語言的 CASE 語法產生混淆,本身結合表示式進行條件分支判斷,是許多資料分析師在日常工作中最長用的套路。
現在使用簡單 SQL 建立應用的場景越來越少了,但 BI 場景下,基於 SQL 的增強表示式場景越來越多了,本系列我就是以理解 BI 場景下查詢表示式為目標建立的,希望能夠學以致用。
討論地址是:精讀《SQL 入門》· Issue #398 · ascoders/weekly
如果你想參與討論,請 點選這裡,每週都有新的主題,週末或週一釋出。前端精讀 - 幫你篩選靠譜的內容。
關注 前端精讀微信公眾號
<img width=200 src="https://img.alicdn.com/tfs/TB165W0MCzqK1RjSZFLXXcn2XXa-258-258.jpg">
版權宣告:自由轉載-非商用-非衍生-保持署名(創意共享 3.0 許可證)