阿川私房教材:學程式,拿 offer!

63 個專案實戰,直接上手!
無需補習,按步驟打造你的面試作品。

立即解鎖你的轉職秘笈
Judy  ·  12月20日

教科書中 SQL 例句通常都很簡單易懂,甚至可以當英語來讀,這就給人造成 SQL 簡單易學的印象。
但實際上,這種三行五行的 SQL 隻存在於教科書和培訓班,我們在現實業務中寫的 SQL 不會論行,而是以 K 計的,一條 SQL 幾百行 N 層嵌套,寫出 3K5K 是常事,這種 SQL,完全談不上簡單易學,對專業程序員都是惡夢。
以 K 計本身倒不是大問題,需求真地複雜時,也隻能寫得長,Python/Java 代碼可能會更長。但 SQL 的長和其它語言的長不一樣,SQL 的長常常會意味著難寫難懂,而且這個難寫難懂和任務複雜度不成比例。除了一些最簡單情況外,稍複雜些的任務,SQL 的難度就會陡增,對程序員的智商要求很高,所以經常用作應聘考題。

這是為什麼呢?
其中一個原因是我們之前講過的,SQL 像英語而缺乏過程性,要把很多動作攪合在一句中,憑空地增大思維難度。
但是我們會發現,即使 SQL 增加了步驟化的 CTE 語法,麵對稍複雜的任務時,仍然會寫的非常難懂。
這是因為,SQL 的描述能力還有不少重要的缺失,這導致程序員不能按自然思維寫代碼,要換著方法繞。
我們通過一個簡單的例子來看一下。

簡化的銷售業績表 T 有三個字段:sales 銷售員,product 產品,amount 銷售額。我們想知道空調和電視銷售額都在前 10 名的銷售員名單。
這個問題並不難,可以很自然地設計出計算過程:
1.按空調銷售額排序,找出前 10 名;
2.按電視銷售額排序,找出前 10 名;
3.對 1、2 的結果取交集,得到我們想要的

用 CTE 語法後 SQL 可以寫成這樣:

with A as (select top 10 sales from T where product='AC' order by amount desc),
     B as (select top 10 sales from T where product='TV' order by amount desc)
select * from A intersect B

這個句子不太短,但思路還是清晰的。

現在,我們把問題複雜化一點,改為計算所有產品銷售額都在前 10 名的銷售員,延用上述的思路很容易想到:

  1. 列出所有產品;
  2. 算出每種產品銷售額的前 10 名,分別保存;
  3. 針對這些前 10 名取交集;
    遺憾開始出現,CTE 語法隻能寫出確定個數的中間結果。而我們事先不知道總共有多個產品,也就是說 WITH 子句的個數是不確定的,這就寫不出來了。
    好吧,換一種思路:
    1.將數據按產品分組,將每組排序,計算出每組前 10 名;
    2.針對這些前 10 名取交集;
    這需要把第一步的分組結果保存起來,而這個中間結果是一個表,其中有個字段要存儲對應的分組成員的前 10 名,也就是字段的取值將是個集合,SQL 不支持這種數據類型,還是寫不出來。

我們可以再轉換思路。按產品分組後,計算每個銷售員在所有分組的前 10 名中出現的次數,若與產品總數相同,則表示該銷售員在所有產品銷售額中均在前 10 名內。

select sales from ( 
    select sales from (
        select sales, rank() over (partition by product order by amount desc ) ranking
        from T ) where ranking <=10 )
group by sales having count(*)=(select count(distinct product) from T)

在窗口函數支持下,終於能寫出來了。但是,這樣的思路,繞不繞呢,有多少人想到並寫出來呢?
前兩種簡單的思路無法用 SQL 實現,隻能采用第三種迂回的思路。這裏的原因在於 SQL 的一個重要缺失:集合化不徹底。
SQL 有集合概念,但並未把集合作為一種基礎數據類型提供,不允許字段取值是集合,除了表之外也沒有其它集合形式的數據類型,這使得大量集合運算在思維和書寫時都非常繞。

我們剛才用了關鍵字 top,事實上關係代數理論中沒有這個東西,這不是 SQL 的標準寫法。
沒有 top 如何找前 10 名呢?
大體思路是這樣:找出比自己大的成員個數作為是名次,然後取出名次不超過 10 的成員

select sales from (
    select A.sales sales, A.product product,
        (select count(*)+1 from T
         where A.product=product and A.amount<=amount) ranking
    from T A )where product='AC' and ranking<=10

注意,這裏的子查詢沒辦法用 CTE 語法分步寫,因為它用到了主查詢中的信息作為參數。

或可以用連接來寫,這樣子查詢倒是可以用 CTE 語法分步了:

select sales from (
    select A.sales sales, A.product product, count(*)+1 ranking from T A, T B
    where A.sales=B.sales and A.product=B.product and A.amount<=B.amount
    group by A.sales,A.product )
where product='AC' and ranking<=10

無論如何,這種東西都太繞了,專業程序員也要想一陣子,僅僅是計算了一個前 10 名。

造成這個現象的原因就是 SQL 的另一個缺失:缺乏有序支持。SQL 繼承了數學上的無序集合,與次序有關的計算相當困難,而可想而知,與次序有關的計算會有多麼普遍(諸如比上月、比去年同期、前 20%、排名等)。
SQL2003 標準中增加的窗口函數提供了一些與次序有關的計算能力,這在一定程度上緩解 SQL 有序計算的困難,前 10 名可以這樣寫:

select sales from ( 
    select sales, rank() over (partition by product order by amount desc ) ranking
    from T )
where ranking <=10

還是要用子查詢。

窗口函數並沒有根本改變 SQL 無序集合的基礎,還是會有許多有序運算難以解決。比如我們經常用來舉例的,計算一支股票最長連續上漲了多少天:

select max(ContinuousDays) from (
    select count(*) ContinuousDays from (
        select sum(UpDownTag) over (order by TradeDate) NoRisingDays from (
            select TradeDate,case when Price>lag(price) over ( order by TradeDate) then 0 else 1 end UpDownTag from Stock ))
    group by NoRisingDays )

自然思維是這樣,按日期排序後開始計數,碰到漲了就加 1,跌了就清 0,看計數器最大計到幾。但這個思路寫不出 SQL,隻能繞成這樣多層嵌套的。
這個問題真地是當作應聘考題的,通過率不到 20%。

這麼一個簡單的例子就能暴露出 SQL 缺失的能力,SQL 缺失的內容還有更多,限於篇幅,這裏就不再深入討論了。
反正結果就是,SQL 實現查詢時無法應用自然思路,經常需要繞路迂回,寫得又長又難懂。
現實任務要遠遠比這些例子複雜,過程中會麵臨諸多大大小小的困難。這個問題繞一下,那個問題多幾行,一個稍複雜的任務寫出幾百行多層嵌套的 SQL 也就不奇怪了,過兩月自己也看不懂也不奇怪了。
事實上 SQL 一點也不容易。

SQL 很難寫怎麼辦?用 esProc SPL!
esProc SPL 是個 Java 寫的開源軟件,在這裏https://github.com/SPLWare/esProc。
SPL 在 SQL 已有的集合化基礎上增加了離散性,從而獲得了徹底的集合化和有序能力,上麵的例子就 SPL 就可以延用自然思路寫出來:
所有產品銷售額都在前 10 名的銷售員,按產品分組,取每個組的前 10 名再算交集;

T.group(product).(~.top(10;-amount)).isect()

SPL 支持集合的集合,top 也隻是常規的聚合計算,有了這些基礎,實現自然思路很容易。
一支股票最長連續上漲了多少天,隻要按自然思路寫就行了

cnt=0
Stock.sort(TradeDate).max(cnt=if(Price>Price[-1],cnt+1,0))

SPL 有強大的有序計算能力,即使實現和上麵 SQL 同樣的邏輯也非常輕鬆:

Stock.sort(TradeDate).group@i(Price<Price[-1]).max(~.len())

按讚的人:

共有 1 則留言

作者應該是巨型軟體公司的 DBA 背景

我自己在中小企業開發 10+ 年

寫的 SQL 基本還是短短的 不到百行

哈哈


此人尚未填寫簡介。

阿川私房教材:學程式,拿 offer!

63 個專案實戰,直接上手!
無需補習,按步驟打造你的面試作品。

立即解鎖你的轉職秘笈