第24期:聊一聊非等值分組
我們在上一期研究了分組運(yùn)算的實(shí)質(zhì),即將一個集合按某種規(guī)則拆分成若干子集。不過,上期的關(guān)注重點(diǎn)在于還原分組運(yùn)算的步驟,而沒有討論拆分規(guī)則,例子中都是用某些字段(或表達(dá)式)來定義拆分規(guī)則,也就是SQL中使用的方法。
我們把這種拆分方式稱為等值分組。
一、
等值分組在數(shù)學(xué)上的描述,相當(dāng)于在一個集合上定義了一個等價關(guān)系:分組字段(表達(dá)式)相等的成員(記錄)就認(rèn)為等價。
等價關(guān)系是指滿足如下條件的關(guān)系:
1)交換性,若a=b則b=a
2)傳遞性,若a=b,b=c則a=c
3)排他性,對任何a,b,a=b和a!=b有且只有一個成立
可以證明,任何等價關(guān)系一定能把原集合完全劃分成若干個子集,每個子集中的成員互相等價。
完全劃分具有這樣的性質(zhì):
1)沒有空子集
2)原集合的任何成員都屬于且只屬于某一個子集
考查等值分組,我們會發(fā)現(xiàn)它能夠精確地滿足等價關(guān)系的定義,因而等值分組的結(jié)果一定是完全劃分。
二、
有等值分組和完全劃分,那是不是還有非等值分組和不完全劃分?還有沒有別的方式產(chǎn)生完全劃分?這些是否有業(yè)務(wù)意義呢?
答案是肯定的。
三、
比如我們要統(tǒng)計男女員工數(shù)量。我們可以寫成這樣:
- SELECT gender,COUNT(*) FROM employee GROUP BY gender
但如果公司員工全是男性或女性,這個運(yùn)算結(jié)果就只有一行了,那可能就不是我們想要的了。
為解決這個問題,我們可以設(shè)計這樣一種分組方案:先羅列出一個基準(zhǔn)集合,然后將待分組集合成員的某個屬性(字段或表達(dá)式)與基準(zhǔn)集合成員比較,相同者則分到一個子集中,***拆分出來的子集數(shù)量和基準(zhǔn)集合成員數(shù)是相同的。這種分組我們稱為對位分組。
使用對位分組統(tǒng)計男女員工數(shù)量可以寫成這樣:
- a=[男,女] // 基準(zhǔn)集合
- g=employee.align(a,gender) // 設(shè)計函數(shù)align實(shí)現(xiàn)對位分組,拆分集合
- g.new(a(#),~.len()) // 用分組子集計算匯總
可以想象,這種對位分組在日常統(tǒng)計中是很常見的,比如按地區(qū)、按部門統(tǒng)計,都可以事先把基準(zhǔn)集合列出來,而且我們經(jīng)常還要求結(jié)果集必須按基準(zhǔn)集合的次序出現(xiàn),而等值分組不能保證這個次序,還要再排序(排序時還是要提供這個基準(zhǔn)集合,原集合成員屬性中沒有這個信息)。
對位分組可能出現(xiàn)空子集,它也不能保證任何原集合的成員都被拆到某個子集中(比如有些不重要的成員沒有被列入基準(zhǔn)集合),不過對位分組能保證每個成員最多只出現(xiàn)在一個子集中。
四、
我們還能把對位分組推廣成更一般的枚舉分組。
枚舉分組是指,事先指定一組條件,將待分組集合的成員作為參數(shù)計算這批條件,條件成立者都被劃分到與該條件對應(yīng)的一個子集中,結(jié)果集的子集和事先指定的條件一一對應(yīng)。
比如,將員工按年齡段分組統(tǒng)計人數(shù):
- a=[?<=30,?<=40,?>40] // 用?表示要代入的參數(shù)
- g=employee.enum(a,age) // 設(shè)計函數(shù)enum實(shí)現(xiàn)枚舉分組,拆分集合
- ....
顯然,枚舉分組在日常業(yè)務(wù)中也是不少見的。
枚舉分組和對位分組很象,都需要先列出一個基準(zhǔn)集合,事實(shí)上,對位分組就是一種特殊的枚舉分組。不過,不同的是,枚舉分組可能制造出有重復(fù)成員的子集,也就是可重分組。
- a=[?<=30,?>20 && ?<=40,?>50] // 條件有重疊
- g=employee.enum(a,age)
可重分組在實(shí)際業(yè)務(wù)中相對罕見一些,不過了解一下也有助于再次理解分組運(yùn)算的實(shí)質(zhì)。
五、
表面上看,對位分組和枚舉分組和SQL的GROUP BY差別很大,但理解了分組運(yùn)算的本質(zhì)后,就會明白它們其實(shí)是一回事:把某個集合拆分成若干子集。只是拆分的方法各有不同。
還有其它不完全依賴于成員屬性的分組方式,但仍然是一種“把集合拆成子集”的方法,我們在后續(xù)文章會再提及。
六、
還有一個問題,SQL只提供了等值分組,那會不會不夠用呢?用SQL又是如何解決對位分組和枚舉分組問題的?
其實(shí)SQL的運(yùn)算能力是完備的,上述兩種非等值分組都可以轉(zhuǎn)換成等值分組,就是會麻煩一些。
對于對位分組,可以用基準(zhǔn)集合和待分組集合做LEFT JOIN,對這個結(jié)果集再做GROUP BY就可以得到相同的效果。注意一定要用LEFT JOIN,用JOIN可能會失去空子集,用FULL JOIN又會多出基準(zhǔn)集合之外的成員。枚舉分組也是類似,但語句會更復(fù)雜些,要根據(jù)枚舉條件去設(shè)計JOIN的條件,難以給出通用寫法。