第19期:從SQL語法看集合化
SQL作為最常用的結構化數據計算語言,雖然在做一些細致處理時不太方便,但用于描述基本運算還是比Java等高級語言要簡單許多。這是因為SQL是一種集合化的語言,而Java等語言不是。我們下面從SQL的語法上看集合化語言的一些特征,為了方便討論,我們就用Java作為參照語言,其它高級語言是類似的。
集合運算能力
結構化數據經常是批量(以集合形式)出現的,為了方便地計算這類數據,程序設計語言有必要提供足夠的集合運算能力。
Java等高級語言則沒有直接提供集合運算類庫,雖然也有數組(相當于集合)數據類型,但并沒有定義多少基本運算,以至于我們要對數據成員做個簡單地求和也需要寫四五行循環(huán)語句才能完成,而要做過濾、分組聚合等運算則常常要寫出數百行代碼。代碼過長不僅僅是寫起來很繁瑣,而且也不利于理解算法的整體結構,算法過程都湮沒在細節(jié)處理中。
而SQL則提供有較豐富的集合運算,如SUM/COUNT等聚合運算,WHERE用于過濾、GROUP用于分組,也支持針對集合的交、并、差等基本運算。這樣寫出來的代碼就會短小很多。
表達式參數
那么,有了集合運算能力是否就夠了呢?假如,我們?yōu)镴ava這類語言開發(fā)一批的集合運算類庫,是否就可以達到SQL的效果呢?
沒有這么簡單!
我們來看一下過濾運算。過濾通常需要一個條件,把滿足條件的集合成員保留,更技術的說法,是保留條件計算結果為真的成員。在SQL中這個條件是以一個表達式形式出現的,比如寫WHERE x>0,就表示保留那些使得x>0計算結果為真的成員。這個表達式x>0并不是在執(zhí)行這個語句之前先計算好的,而是在針對集合成員遍歷時才計算的。本質上,這個表達式就是一個函數,是一個以當前集合成員為參數的函數。對于WHERE運算而言,相當于把一個用表達式定義的函數用作了WHERE的參數。
Java的語法不能直接支持這種寫法。Java當然也允許把一個函數作為參數傳遞給另一個函數,但寫法要麻煩很多,需要事先定義一個函數,代碼看起來非常臃腫。而直接把表達式寫到函數的參數中,會被先計算出來,而不是針對每個集合成員分別計算。
相比之下,SQL這種用表達式直接定義函數而作為參數傳遞的方法,顯然要簡捷和直觀得多了。
這種寫法有一個術語叫做lambda語法,或者叫函數式語言。
SQL中大量使用了lambda語法。除了過濾這種運算可以說必須要用外,有些并非必須的情況,使用了這種語法形式也會更為簡單。比如聚合函數中可以填入表達式來計算運算后的聚合值,如sum(x*x)計算平方和,這里x*x也是在sum的執(zhí)行過程中再計算的。在不支持lamdba語法時,我們也可以先用集合運算計算出成員平方構成的集合,再針對這個集合進行地求和,但寫法上就不如使用lamdba語法更為直觀,畢竟針對單個成員的表達式要比針對整個集合的計算更容易書寫和理解。
直接引用字段
結構化數據并非簡單的單值,而是帶有字段的記錄。
我們看到,在SQL的表達式參數中引用記錄字段時,大多數情況可以直接使用字段名稱而不必指明字段所屬的記錄,只有在多個同名字段時才需要冠以表名(或表的別名)以示區(qū)分。
再來看Java,即使我們可以容忍事先定義函數來變相實現lambda語法,也只能把當前記錄作為參數傳入這個函數,然后再寫計算式時就總要帶上這個記錄。比如用單價和數量計算金額時,如果用于表示當前成員的參數名為x,則需要寫成 “x.單價*x.數量”。而在SQL中可以更為直觀地寫成 "單價*數量”。
SQL中這些看起來理所當然的語法風格,其實背后并沒有那么簡單,這需要精心設計后才能被解釋程序正確解析和運算。某些支持lambda語法的腳本語言就沒有這個特性,雖然可以用表達式定義函數作為參數傳遞,但必須寫成“x.單價*x.數量”這種啰嗦的形式。有了直接引用字段的語法機制后,才可以說是專門面向結構化數據計算的語言。
動態(tài)數據結構
SQL還能很好地支持動態(tài)數據結構。
結構化數據計算中,返回值經常也是有結構的數據,而結果數據結構和運算相關,沒辦法在代碼編寫之前就先準備好。所以需要支持動態(tài)的數據結構能力。
SQL中任何一個SELECT語句都會產生一個新的數據結構,在代碼中可以隨意添加刪除字段,而不必事先定義結構(類)。Java這類語言則不行,在代碼編譯階段就要把用到的結構(類)都定義好,原則上不能在執(zhí)行過程中動態(tài)產生新的結構。
解釋型語言
動態(tài)數據結構不能在編譯型語言中實現。前面說到的lambda語法也不適合采用編譯型語言來實現。編譯器不能確定這個寫到參數位置的表達式是應該當場計算出表達式的值再傳遞,還是把整個表達式編譯成一個函數傳遞,需要再設計更多的語法符號加以區(qū)分。而解釋型語言則沒有這個問題,作為參數的表達式是先計算還是遍歷集合成員時再計算,可以由函數本身來決定。解釋執(zhí)行是集合化語言的另一個重要特征。