從編程語言設計的角度,如何評價SQL語言?
背景: 初級Web CRUD 程序員, 用過點 ActiveRecord, Django ORM, Ecto, 偶爾看看 JOOQ 的博客
因為是 Erik Meijer (LINQ) 的粉絲, 恨 Haskell 的各種資料庫抽象爛得可以, 所以稍微對這方面有點想法.
--
首先我覺得 SQL 整體上是個撐下太多抽象的 DSL, 其他答案說的"聲明式", "關係代數模型" 只是現在使用的 SQL 一個比較小的子集, 如果分成比較常用的兩部分 DDL, DML 還有事務控制, 用戶許可權管理之類的, 裡面包羅萬象.
從語法設計的角度來看, SQL 也有點反人類, 10 Easy Steps to a Complete Understanding of SQL 里提到的第一點: "SQL syntax is not 「well-ordered"", 當然入門了寫簡單查詢對這些順序一點都不 care, 但是複雜點的話還是會扯到蛋. 而且 GROUP BY 這種用空格隔開但是合在一起才是一個關鍵字對於正常的編程語言來說還是有點奇怪.
角度還可以有很多, 但是對於我這種CRUD搬磚程序員來說最大的問題是:
"為什麼平時不用 SQL".
可能很多人回答的答案估計是: 直接拼 SQL 會有注入
這不是原因之一, 因為平時開發用的不是 SQL, 在一個語言裡面構造另外一個語言就要注意層次, 不要把另外一個語言 value 層面的東西搞成另外一個語言 meta 層面的東西. 這似乎也是原因之一, SQL的最開始是個對關係模型抽象的DSL不好寫業務邏輯, 雖然後來搞得越來越像其他 GPL (
問題變更精細點:
"為什麼平時不用 SQL, 為什麼要用 ORM/Query Builder whatever 這些東西"
可能有人的答案是估計是: Object-relational impedance mismatch .
in-memory 裡面表達關係模型比較蛋疼, in-memory 最方便的數據表達是: 標量(scalar), 序列(Sequence), 映射(Mapping), 參考 YAML Ain』t Markup Language (YAML?) Version 1.2 , 其實 JSON 就是 YAML 的子集, 所以 MongoDB 這種不用考慮資料庫範式, 跟操作 in-memory 數據的各種編程語言搭配得特別好, 寫得特別無腦. 這個方面講 SQL 的出發點是關係模型的DSL, 跟擅長處理 in-memory 數據的各種 GPL 比起來肯定是弱弱的了, 當然後來各種拓展撐成胖子了.
但是作為一個 Haskell 荼毒的人我的答案是:
1. SQL 不 Compose.
舉個很常見的例子, 各種組合的過濾條件, 條件或有或無
City
|&> where([c], c.country == "Sweden")
|&> or_where([c], c.country == "Brazil")
Elixir 的 Ecto 可以用這種 pipline 的方式來組合條件, 用 ActiveRecord 或者 Django ORM就 chain 下 filter 好了. 如果用這種 SQL 沒啥"結構"的語言搞起來就很蛋疼了.
2. SQL 不是完全編譯時安全的(當然這只是 Haskell 的程序員的一廂情願)
如果一個"父查詢"訪問了一個不存在子查詢返回來的結構裡面的欄位, 能不能在編譯時報個錯?
更抽象點, 能不能在寫查詢的時候就可以知道關係模型的信息, 在查詢執行之前就發現錯誤? 或者說能不能把關係模型 encode 在類型裡面?
當然 SQL 這種弱雞類型的語言是搞不了這種東西的, 但是連 Haskell 這種語言搞起來也是很困難, 就像 Stephen Diehl? 說的: type-safe query synthesis is really hard. https://twitter.com/smdiehl/status/694655182349537280
喜歡SQL。因為它有我喜歡的語言的幾個要素:
1. 紮實的數學基礎
SQL的數學基礎是關係代數,你所編寫的SQL語句最終都可以翻譯為關係代數上的運算。這種紮實的數學基礎可以使語言具有良好而自洽的表達能力,同時不會因為一些不合理的Ad hoc設計而處處留坑。(數學基礎不強的語言基本上都有很多坑,比如早期的PHP)
另外,你可以重新發明很多種SQL的方言(真的,Google裡面就有好幾種)但萬變不離其宗,畢竟你不能重新發明關係代數。
具有類似性質的好幾門語言,我都挺喜歡,比如:
- LISP,背後是λ演算,這個數學基礎給了LISP非常強大的表達能力;(雖然多數人不直接用LISP,但挺值得了解一下)至少,LISP給現在各種支持函數式編程的語言提供了借鑒;
- 正則表達式。背後是正則文法。凡是可以使用正則文法定義的語言,都可以使用正則表達式定義。當然,可能因為正則表達式太成功,經常有人試圖用它來匹配各種編程語言的代碼,這基本上是肯定要出bug的。原因很簡單,多數主流編程語言都是『上下文無關語言』,它是正則語言的超集;
- BNF,背後是上下文無關文法。這也是為什麼各種編程語言(即使複雜如C++或C#,還包括SQL和正則表達式)的spec,甚至不少『標準格式』(如JSON,URI等)的spec 都喜歡用BNF或EBNF定義。更好玩的是,當你用BNF定義好一門語言時,還可以使用一種稱為編譯器之編譯器(Compiler"s Compiler)的程序(如YACC及各語言上的移植,ANTLR等)來生成這門語言的解析程序!為什麼能做到這麼利害的功能?這涉及到編譯原理的很多知識,但歸根到底,就是上下文無關文法的數學基礎。
2. 平易近人的語法糖衣
SQL以自然語言英語為藍本設計,易學易記,很多非專業編程人員也能很快掌握。(不會編程但會寫SQL的,我們把他們稱為數據分析師 (逃))不要當作這點是理所當然的。同樣基於關係代數,你可以基於LISP採用的S-expression來設計一門有與SQL同樣表達能力的語言,還可以基於JSON來設計一門有與SQL同樣表達能力的語言(比如Mongo DB的JSON API,如果你把它看作一門語言的話)但非專業編程人員可能就沒有那麼容易上手了。
3. 解決了重要的問題
SQL解決了結構化數據的查詢和更新問題。這種能力使得它在編程界幾乎無處不在。你的手機上可能跑著很多個SQLlite的資料庫;你訪問的很多中小型網站,可能跑著很多MySQL資料庫。你存錢的銀行,很可能跑著許多Oracle的資料庫。這些資料庫都主要以SQL作為查詢和操作數據的語言。就算強如Google,能夠設計出有全球擴展性和異地容災的分散式資料庫F1(見https://research.google.com/pubs/pub38125.html ),也得乖乖地提供SQL語言的支持。
4. 高級聲明式語言
SQL通常被j認為是第四代編程語言,語言每過一代通過意味著它有高一個層次的抽象(抽象層次:機器語言 &< 彙編語言 &< 多數高級編程語言 &< SQL)注意抽象的層次和語言是否優秀並沒有必然關係,也不意味著高抽象層次的語言可以完全替代低抽象層次的語言。但是高級的抽象往往意味著編程人員可以更少地關心實現細節,更多地關心業務邏輯的表達。使用SQL時,普通用戶主要關心的是如何表達查詢的邏輯,也就等價於關係代數上的運算。至於這種運算如何翻譯成具體的執行計劃(Execution Plan),使用哪些演算法和數據結構進行高效存取和運算則交給了資料庫去完成。當然,高級用戶也可以通過各種手段去優化SQL的執行(比如表設計、建立合適的索引、改寫優化器無法良好優化的查詢等)。
-------------------------
(現在是我這邊深夜了,下次再更)
我嘗試用Haskell和C++無論如何玩魔法都沒辦法做出像sql那樣可以有上下文有關優化的查詢,所以我覺得sql雖然那麼複雜,但是作為一個dsl還是有很大的存在的價值的。
公正中立的講sql是一個沒有對象的語言所以我並不喜歡畢竟程序員本來就已經很缺對象了
為了看上去像英語,加了好多語法糖。BASIC也這麼干過。
各種WEB後台技術,到現在還沒出現可以完全(高效)抽象關係模型的ORM,關鍵時刻,還是SQL直接快速易理解。
SQL是專門處理記錄集的語言,與平常的編程語言有很大的不同。基本都符合標準,但在分頁等實現細節上有些微的方言,各家實現時也附加了非標準的過程語言功能。對於記錄集的關聯、過濾,它基本是最高效的了。但寫法不同(包括索引的配合設置),可能導致幾個數量級的效率差異。
舉自己做過的一些例子吧:
1)複雜的:某系統,會員每天可以簽到一次。簽到時,提示會員:你已連續簽到3天,共簽到40天,你的最大連續簽到天數6天;所有會員中,最高連續簽到天數紀錄是XXX創造的12天,最大簽到天數紀錄是XXXX創造的60天。
表的欄位包括會員ID和簽到日期(無其他欄位)。至於那個顯示結果,我是用一句sql解決問題的(但花了約15分鐘調試)。
如果不用sql,不知道要寫幾個屏幕的程序?寫出來運行的效率,會不會讓用戶抓狂?
2)簡單的:某提醒系統,所有患者都記錄了開始服藥日期和嚴重程度。要求系統在每個患者開始服藥的第2周,第4周,第8周,第14周,第28周要提醒其體檢。如果是嚴重的病人,要求第56周再增加一次提醒。
同樣,寫一句簡單sql,問題就可以解決(正常水平2分鐘之內)。寫程序,耗時長,運行效率低。
SQL有點類似於正則表達式(當然複雜得多)——沒有,也能自己做出來;但是,如果掌握了,那是程序員利器。
SQL是很高級的語言,因為他只描述問題,並不需要告訴計算機怎麼解決。當然它的適用面很窄,但關係型資料庫暫時還都離不了。
之前看過很多書,都說SQL是第四代語言。就是因為它的這種描述性。假如其他的編程語言也這樣那就大大的降低開發的門檻了,畢竟準確的描述問題,大多數人還是可以做到的。我不認為SQL是程序設計語言,它的理論基礎是集合論,和常見的c之類的語言不是一個層面的東西。
但是如果拋開上述因素,從程序設計語言的角度看的話,它是最高級別的聲明式語言。從統計上來講,sql一句相當於40句彙編,java相當於5.4,所以說sql是一門非常好的語言。由於sql是面向數學的語言,而不是面向馮諾依曼機的語言,能夠很好的屏蔽實現細節,我非常喜歡,謝謝大家。當然你們也不難看出,我認為c++就是坨屎。
以通用語言的角度來說,必須不好用。
以查詢DSL來說,脫離其他語言生成的情況,SQL本身不DRY,也不好寫複雜查詢。(試試幾個稍微複雜點的查詢就一大片UNION)
以RMDBS本身的角度來說,SQL本身就是過度設計的產物。我就想存個東西,你給我來個圖靈完整的編程語言?因為很多邏輯被放到RMDBS里跑,你就必須要關心很多索引問題,N + 1問題,一致性問題,擴展問題,而且還必須手動優化,最終也沒法解決分散式ACID。
感覺未來的趨勢就是像Event sourcing那樣讓模型活在內存里(比如用Actor管理),通用語言的程序自己管理才是王道。
我只感覺SQL好難學
似乎不止我一人受不了SQL的一致性
這個你要和tutorial D來對比。SQL不是那麼數學,他比較工程。從編程語言設計角度來說,並沒有tutorial D這樣的一致性。
不太懂編程語言的設計這個問題呢。
但是有一點,SQL不是圖靈完備的。所以拿他和普通的程序語言進行比較,去比較圖靈完備的語言里的各種機制,各種關鍵字和語法設計的簡潔優美程度,我認為SQL是沒有可比性的。最原始的SQL是一階邏輯完備的。每一個一階邏輯表達式都可以用一句SQL表達。但SQL的表達能力大概也就這樣。遞歸查詢SQL是做不到的。
所以評價SQL這門語言怎麼樣,從某種程度上可以從另一個角度來思考,關係模型和SQL查詢,這套模型怎麼樣
————————————————————————————————————
http://www.cs.cmu.edu/~pavlo/courses/fall2013/static/papers/whatgoesaround-stonebraker.pdf今年圖靈獎得主寫的文章,當然不一定是今年寫的。介紹資料庫模型和對應的查詢語言的演變歷史。我也看得不是很仔細。。。摘錄一些內容:關於關係模型的那一段的總結:
SQL的成功,源自於「elephants of the marketplace」,當時指的就是IBM做出了產品。
SQL的查詢優化機制生成的Query Plan,比起程序員們瞎寫Query Plan怎麼都是靠譜的。另一方面:這段分頁了,內容是連在一塊的。早在80年代,就有人吐槽DBMS不給力,關係模型不給力了。王垠啊,naive
這玩意不是資料庫里用來查詢用的嗎?雖然可以用where,聚合域函數和銜套,理想狀態下是只用一個select就可以把複雜查詢寫出來。但是可能是我水平不行,經常最後還是用了for,do這樣的循環語句。
SQL是屬於聲明式編程範式語言,dsl的一種。目前討論的大多數編程語言如Java c sharp c 等主要屬於命令式編程範式語言。
SQL 是集合操作的語言,編寫思路角度和其他語言不同。
哦,大家的回答都好高深,我才學這個。當初第一次看到這個語言,唯一的感覺就是比c和Java沒道理多了!
推薦閱讀:
※一條LEFT JOIN+ORDER BY的sql語句優化問題?
※為什麼MySQL對SQL標準的支持那麼不誠意?
※SQL 設計得爛嗎,諸如redis,nosql又該如何選擇?
※什麼是最好的oracle sql 開發工具?
※sql server中如何儘可能高效地把表導出成excel,有好幾億條數據?