第31期:JOIN簡化 - 維度對齊
我們先把上一期中雙子表對齊例子的SQL寫出來:
- SELECT Orders.id, Orders.customer, A.x, B.y FROM Orders
- LEFT JOIN (SELECT id,SUM(price) x FROM OrderDetail GROUP BY id ) A ON Orders.id=A.id
- LEFT JOIN (SELECT id,SUM(amount) y FROM OrderPayment GROUP BY id ) B ON Orders.id=B.id
- WHERE A.x > B.y
那么問題來了,這顯然是個有業(yè)務(wù)意義的JOIN,它算是前面所說的哪一類呢?
這個JOIN涉及了表Orders和子查詢A與B,仔細(xì)觀察會發(fā)現(xiàn),子查詢帶有GROUP BY id的子句,顯然,其結(jié)果集將以id為主鍵。這樣,JOIN涉及的三個表(子查詢也算作是個臨時(shí)表)的主鍵是相同的,它們是一對一的同維表,仍然在前述的范圍內(nèi)。
一
但是,這個同維表JOIN卻不能用上一期說的寫法簡化,子查詢A,B都不能省略不寫。
可以簡化書寫的原因在于:我們假定事先知道數(shù)據(jù)結(jié)構(gòu)中這些表之關(guān)聯(lián)關(guān)系。用技術(shù)術(shù)語的說法,就是知道數(shù)據(jù)庫的元數(shù)據(jù)(metadata)。而對于臨時(shí)產(chǎn)生的子查詢,顯然不可能事先定義在元數(shù)據(jù)中了,這時(shí)候就必須明確指定要JOIN的表(子查詢)。
不過,雖然JOIN的表不能省略,但關(guān)聯(lián)字段總是主鍵,已經(jīng)在GROUP BY中寫過了,就沒有必要再寫一遍了;而且,子查詢的主鍵總是由GROUP產(chǎn)生,而GROUP BY的字段一定要被選出用于做外層JOIN,也沒必要在GROUP和SELECT中各寫一次;并且這幾個子查詢涉及的子表是互相獨(dú)立的,它們之間不會再有關(guān)聯(lián)計(jì)算了,我們就可以把GROUP動作以及聚合式直接放到主句中,從而消除一層子查詢:
- SELECT Orders.id, Orders.customer, OrderDetail.SUM(price) x, OrderParyment.SUM(amount) y
- FROM Orders LEFT JOIN OrderDetail GROUP BY id LEFT JOIN OrderPayment GROUP BY id
- WHERE A.x > B.y
二
這里的JOIN和SQL定義的JOIN運(yùn)算已經(jīng)差別很大,完全沒有笛卡爾積的意思了。而且,也不同于SQL的JOIN運(yùn)算將定義在任何兩個表之間,這里的JOIN,OrderDetail和OrderPayment以及Orders都是向共同的主鍵id靠攏,即所有表都向某一套基準(zhǔn)維度對齊。而由于各表的維度(主鍵)不同,對齊時(shí)可能會有GROUP BY,在引用該表字段時(shí)就會相應(yīng)地出現(xiàn)聚合運(yùn)算。OrderDetail和OrderPayment甚至Orders之間都不直接發(fā)生關(guān)聯(lián),在書寫運(yùn)算時(shí)當(dāng)然就不用關(guān)心它們之間的關(guān)系,甚至不必關(guān)心另一個表是否存在。而SQL那種笛卡爾積式的JOIN則總要找一個甚至多個表來定義關(guān)聯(lián),一旦減少或修改表時(shí)就要同時(shí)考慮關(guān)聯(lián)表,增大理解難度。
我們稱這種JOIN稱為維度對齊,它并不超出我們前面說過的三種JOIN范圍,但確實(shí)在語法描述上會有不同,這里的JOIN不象SQL中是個動詞,卻更象個連詞。而且,和前面三種基本JOIN中不會或很少發(fā)生FULL JOIN的情況不同,維度對齊的場景下FULL JOIN并不是很罕見的情況。
三
雖然我們從主子表的例子抽象出維度對齊,但這種JOIN并不要求JOIN的表是主子表(事實(shí)上從上一篇的語法可知,主子表運(yùn)算還不用寫這么麻煩),任何多個表都可以這么關(guān)聯(lián),而且關(guān)聯(lián)字段也完全不必要是主鍵或主鍵的部分。
設(shè)有合同表,回款表和發(fā)票表:
現(xiàn)在想統(tǒng)計(jì)每一天的合同額、回款額以及發(fā)票額,就可以寫成:
- SELECT Contract.SUM(price), Payment.SUM(amount), Invoice.SUM(amount)
- FROM Contract GROUP BY date FULL JOIN Payment GROUP BY date FULL JOIN Invoice GROUP BY date
四
這幾種JOIN情況還可能混合出現(xiàn)。
延用上面的合同表,再有客戶表和銷售員表
其中Contract表中customer字段是指向Customer表的外鍵。
現(xiàn)在我們想統(tǒng)計(jì)每個地區(qū)的銷售員數(shù)量及合同額:
- SELECT Sales.COUNT(1), Contract.SUM(price)
- FROM Sales GROUP BY area FULL JOIN Contract GROUP BY customer.area
維度對齊可以和外鍵屬性化的寫法配合合作。
五
這些例子中,最終的JOIN都是同維表。事實(shí)上,維度對齊還有主子表對齊的情況,不過相對罕見,我們將在后續(xù)仔細(xì)講解維度概念時(shí)再涉及,上述寫法中其實(shí)還有個小漏洞,有了明確的維度定義后才能將這個漏洞補(bǔ)上。