天天看點

資料蔣堂 | JOIN簡化 - 次元對齊

我們先把上一期中雙子表對齊例子的SQL寫出來:

那麼問題來了,這顯然是個有業務意義的JOIN,它算是前面所說的哪一類呢?

這個JOIN涉及了表Orders和子查詢A與B,仔細觀察會發現,子查詢是帶有GROUP BY id的子句,顯然,其結果集将以id為主鍵。這樣,JOIN涉及的三個表(子查詢也算作是個臨時表)的主鍵是相同的,它們是一對一的同維表,仍然在前述的範圍内。

但是,這個同維表JOIN卻不能用上一期說的寫法簡化,子查詢A,B都不能省略不寫。

可以簡化書寫的原因在于:我們假定事先知道資料結構中這些表的關聯關系。用技術術語的說法,就是知道資料庫的中繼資料(metadata)。而對于臨時産生的子查詢,顯然不可能事先定義在中繼資料中了,這時候就必須明确指定要JOIN的表(子查詢)。

不過,雖然JOIN的表不能省略,但關聯字段總是主鍵,已經在GROUP BY中寫過了,就沒有必要再寫一遍了;而且,子查詢的主鍵總是由GROUP産生,而GROUP BY的字段一定要被選出用于做外層JOIN,也沒必要在GROUP和SELECT中各寫一次;并且這幾個子查詢涉及的子表是互相獨立的,它們之間不會再有關聯計算了,這樣我們就可以把GROUP動作以及聚合式直接放到主句中,進而消除一層子查詢:

這裡的JOIN和SQL定義的JOIN運算已經差别很大,完全沒有笛卡爾積的意思了。而且,也不同于SQL的JOIN運算将定義在任何兩個表之間,這裡的JOIN,OrderDetail和OrderPayment以及Orders都是向共同的主鍵id靠攏,即所有表都向某一套基準次元對齊。而由于各表的次元(主鍵)不同,對齊時可能會有GROUP BY,在引用該表字段時就會相應地出現聚合運算。

OrderDetail和OrderPayment甚至Orders之間都不直接發生關聯,在書寫運算時當然就不用關心它們之間的關系,甚至不必關心另一個表是否存在。而SQL那種笛卡爾積式的JOIN則總要找一個甚至多個表來定義關聯,一旦減少或修改表時就要同時考慮關聯表,這就增大了了解難度。

我們把這種JOIN稱為次元對齊,它并不超出我們前面說過的三種JOIN範圍,但确實在文法描述上會有不同,這裡的JOIN不象SQL中是個動詞,卻更象個連詞。而且,和前面三種基本JOIN中不會或很少發生FULL JOIN的情況不同,次元對齊的場景下FULL JOIN并不是很罕見的情況。

雖然我們從主子表的例子抽象出次元對齊,但這種JOIN并不要求JOIN的表是主子表(事實上從上一篇的文法可知,主子表運算還不用寫這麼麻煩),任何多個表都可以這麼關聯,而且關聯字段也完全不必要是主鍵或主鍵的部分。

設有合同表,回款表和發票表:

資料蔣堂 | JOIN簡化 - 次元對齊

現在想統計每一天的合同額、回款額以及發票額,就可以寫成:

這幾種JOIN情況還可能混合出現。

延用上面的合同表,再有客戶表和銷售員表

資料蔣堂 | JOIN簡化 - 次元對齊
資料蔣堂 | JOIN簡化 - 次元對齊

其中Contract表中customer字段是指向Customer表的外鍵。

現在我們想統計每個地區的銷售員數量及合同額:

次元對齊可以和外鍵屬性化的寫法配合合作。

這些例子中,最終的JOIN都是同維表。事實上,次元對齊還有主子表對齊的情況,不過相對罕見,我們将在後續仔細講解次元概念時再涉及,上述寫法中其實還有個小漏洞,有了明确的次元定義後才能将這個漏洞補上。

原文釋出時間為:2017-11-23

本文作者:蔣步星