阿川私房教材:
學 JavaScript 前端,帶作品集去面試!

63 個專案實戰,寫出作品集,讓面試官眼前一亮!

立即開始免費試讀!
Judy  ·  12月23日

我們研究過分組運算的實質,即將一個集合按某種規則拆分成若幹子集。不過,討論重點在於還原分組運算的步驟,並沒有討論拆分規則,例子中都是用某些分組鍵值來定義拆分規則,也就是 SQL 中使用的方法。
我們把這種拆分方式稱為等值分組。

等值分組相當於在集合上定義了一個等價關係:分組鍵值相等的成員(記錄)就認為等價。等價關係是個數學概念,這裏不詳述了。可以證明,任何等價關係一定能把原集合完全劃分成若幹個子集,每個子集中的成員互相等價。

完全劃分具有這樣兩個性質:
1) 沒有空子集
2) 原集合的任何成員都屬於且隻屬於某一個子集
考查等值分組,我們會發現這種分組的結果能精確的滿足完全劃分的特性。

有等值分組和完全劃分,那是不是還有非等值分組和不完全劃分?還有沒有別的方式產生完全劃分?這些是否有業務意義呢?

答案是肯定的。

比如我們要統計男女員工數量。我們可以寫成這樣:

SELECT gender,COUNT(*) FROM employee GROUP BY gender

但如果公司員工全是男性或女性,這個運算結果就隻有一行了,那可能就不是我們想要的了。

為解決這個問題,我們可以設計這樣一種分組方案:先羅列出一個基準集合,然後將待分組集合成員的某個鍵值與基準集合成員比較,相同者則分到一個子集中,最後拆分出來的子集數量和基準集合成員數是相同的。這種分組我們稱為對位分組。

SPL 中提供了對位分組運算,用來統計男女員工數量可以寫成這樣:

a=[Male,Female]                 // 基準集合
g=employee.align(a,gender)          // 函數align實現對位分組,拆分集合
g.new(a(#),~.len())             // 用分組子集計算彙總

可以想象,這種對位分組在日常統計中是很常見的,比如按地區、按部門統計,都可以事先把基準集合列出來,而且我們經常還要求結果集必須按基準集合的次序出現,等值分組就不能保證這個次序,還要再排序,而排序時還是要提供這個基準集合,因為原集合成員屬性中沒有這個信息。

對位分組可能出現空子集,它也不能保證任何原集合的成員都被拆到某個子集中(比如有些不重要的成員沒有被列入基準集合),不過對位分組能保證每個成員最多隻出現在一個子集中。

我們還能把對位分組推廣成更一般的枚舉分組。
枚舉分組是指,事先指定一組條件,將待分組集合的成員作為參數計算這批條件,條件成立者都被劃分到與該條件對應的一個子集中,分組結果中的子集和事先指定的條件一一對應。

比如,將員工按年齡段分組統計人數,SPL 中可以這樣寫:

a=[?<=30,?<=40,?>40]            // 用?表示要代入的參數
g=employee.enum(a,age)          // 設計函數enum實現枚舉分組,拆分集合
....

顯然,枚舉分組在日常業務中也是不少見的。

枚舉分組和對位分組很象,都需要先列出一個基準集合,事實上,對位分組就是一種特殊的枚舉分組。不過,不同的是,枚舉分組可能製造出有重複成員的子集,也就是可重分組。

a=[?<=30,?>20 && ?<=40,?>50]        // 條件有重疊
g=employee.enum@r(a,age)

可重分組在實際業務中相對罕見一些,不過了解一下也有助於再次理解分組運算的實質。

表麵上看,對位分組和枚舉分組和 SQL 的 GROUP BY 差別很大,但理解了分組運算的本質後,就會明白它們其實是一回事:把某個集合拆分成若幹子集。隻是拆分的方法各有不同。

還有其它不完全依賴於成員屬性的分組方式,但仍然是一種“把集合拆成子集”的方法,我們還會再講到。

還有一個問題,SQL 隻提供了等值分組,那會不會不夠用呢?用 SQL 又是如何解決對位分組和枚舉分組問題的?

其實 SQL 的運算能力是完備的,上述兩種非等值分組都可以轉換成等值分組,但是會麻煩很多。
對於對位分組,可以用基準集合和待分組集合做 LEFT JOIN,對這個結果集再做 GROUP BY 就可以得到對位分組的效果了。注意一定要用 LEFT JOIN,用 JOIN 可能會失去空子集,用 FULL JOIN 又會多出基準集合之外的成員。

枚舉分組也是類似,但語句會更複雜,要根據枚舉條件去設計 JOIN 的條件,難以給出通用寫法。

按讚的人:

共有 0 則留言


此人尚未填寫簡介。

阿川私房教材:
學 JavaScript 前端,帶作品集去面試!

63 個專案實戰,寫出作品集,讓面試官眼前一亮!

立即開始免費試讀!