第32期:JOIN簡化 - 意義總結(jié)
我們重新審視和定義了等值JOIN運(yùn)算,并簡化了語法。一個(gè)直接的效果顯然是讓語句書寫和理解更容易。外鍵屬性化、同維表等同化和主子表一體化方案直接消除了顯式的關(guān)聯(lián)運(yùn)算,也更符合自然思維;維度對齊則可讓程序員不再關(guān)心表間關(guān)系,降低語句的復(fù)雜度。
一
簡化JOIN的好處不僅在于此,還能夠降低出錯率。
我們知道,SQL允許用WHERE來寫JOIN運(yùn)算的過濾條件(回到原始的笛卡爾積式的定義),很多程序員也習(xí)慣于這么寫。當(dāng)JOIN表只有兩三個(gè)的時(shí)候,那問題還不大,但如果JOIN表有七八個(gè)甚至十幾個(gè)的時(shí)候,漏寫一個(gè)JOIN條件是很有可能的。而漏寫了JOIN條件意味著將發(fā)生多對多的完全叉乘,而這個(gè)SQL卻可以正常執(zhí)行,一方面計(jì)算結(jié)果會出錯(回憶一下以前說過的,發(fā)生多對多JOIN時(shí),大概率是語句寫錯了),另一方面,如果漏寫條件的表很大,笛卡爾積的規(guī)模將是平方級的,這極有可能把數(shù)據(jù)庫直接“跑死”!
采用簡化后的JOIN語法,就不可能發(fā)生漏寫JOIN條件的情況了。因?yàn)閷OIN的理解不再是以笛卡爾積為基礎(chǔ),而且設(shè)計(jì)這些語法時(shí)已經(jīng)假定了多對多關(guān)聯(lián)沒有業(yè)務(wù)意義,這個(gè)規(guī)則下寫不出完全叉乘的運(yùn)算。
對于多個(gè)子表分組后與主表對齊的運(yùn)算,在SQL中要寫成多個(gè)子查詢的形式。但如果只有一個(gè)子表時(shí),可以先JOIN再GROUP,這時(shí)不需要子查詢。有些程序員沒有仔細(xì)分析,會把這種寫法推廣到多個(gè)子表的情況,也先JOIN再GROUP,可以避免使用子查詢,但計(jì)算結(jié)果是錯誤的。
使用維度對齊的寫法就不容易發(fā)生這種錯誤了,無論多少個(gè)子表,都不需要子查詢,一個(gè)子表和多個(gè)子表的寫法完全相同。
二
重新看待JOIN運(yùn)算,最關(guān)鍵的作用在于實(shí)現(xiàn)關(guān)聯(lián)查詢。
當(dāng)前敏捷BI產(chǎn)品非常火熱,各家產(chǎn)品都宣稱能夠讓業(yè)務(wù)人員拖拖拽拽就完成想要的查詢報(bào)表。但實(shí)際應(yīng)用效果會遠(yuǎn)不如人意,業(yè)務(wù)人員仍然要經(jīng)常求助于IT部門。造成這個(gè)現(xiàn)象的主要原因在于大多數(shù)業(yè)務(wù)查詢都是有過程的計(jì)算,不大可能由直接不會編程的業(yè)務(wù)人員獨(dú)立完成。但是,仍有約三成左右的業(yè)務(wù)查詢并不涉及多步過程,而業(yè)務(wù)人員仍然無法完成。
這是由于大多數(shù)敏捷BI產(chǎn)品(以及多年前流行的OLAP產(chǎn)品)都不支持關(guān)聯(lián)查詢。這些產(chǎn)品的工作模式是先由技術(shù)人員構(gòu)建模型,再由業(yè)務(wù)人員基于模型在界面上進(jìn)行查詢。而所謂建模,其實(shí)就是生成一個(gè)邏輯上或物理上的單表,業(yè)務(wù)人員只能在這個(gè)單表的范圍內(nèi)查詢分析,無論界面做得多么流暢炫酷,在數(shù)據(jù)獲取層面都不可能超越這個(gè)事先構(gòu)建好的單表范圍。用戶的查詢需求一旦超出了這個(gè)單表,需要關(guān)聯(lián)到其它表中數(shù)據(jù)時(shí),就要由技術(shù)人員再次建模。建模實(shí)際上要針對不同的關(guān)聯(lián)需求分別實(shí)現(xiàn),我們稱之為按需建模。但實(shí)際上,有意義的查詢絕大多數(shù)都有關(guān)聯(lián)需求,技術(shù)人員也不可能事先預(yù)測所有的關(guān)聯(lián),就算預(yù)測了也不可能把所有的關(guān)聯(lián)可能性都事先做好。結(jié)果是,要么建模動作頻頻發(fā)生,要么業(yè)務(wù)用戶沒法使用,無論如何,這些敏捷BI產(chǎn)品都會失去敏捷性。
為什么這些BI產(chǎn)品不能支持關(guān)聯(lián)查詢呢?因?yàn)椴⒉蝗菀?,其根源就在于SQL對JOIN的定義過于簡單,導(dǎo)致表間關(guān)聯(lián)過于繁瑣,超出業(yè)務(wù)人員的理解能力,直接把數(shù)據(jù)結(jié)構(gòu)暴露出來由業(yè)務(wù)用戶自己完成JOIN運(yùn)算是不可能的。有些BI產(chǎn)品的界面協(xié)助下有一些改善,在事先定義好維度后,可以讓業(yè)務(wù)人員正確處理沒有形成環(huán)的關(guān)聯(lián)關(guān)系以及同表內(nèi)沒有相同維度的關(guān)聯(lián)情況,全自關(guān)聯(lián)(形成環(huán))和同表多同維字段仍需要再建模去解決,這些細(xì)節(jié)我們也留到講述維度概念時(shí)來再詳細(xì)討論。
但是,如果改變了對JOIN運(yùn)算的看法,關(guān)聯(lián)查詢可以從根本上得到解決?;貞浨懊嬷v過的三種JOIN及其簡化手段,我們事實(shí)上把這幾種情況的多表關(guān)聯(lián)都看成了單表查詢,而業(yè)務(wù)用戶對于單表查詢并沒有理解障礙。無非就是表的屬性(字段)稍復(fù)雜了一些:可能有子屬性(外鍵字段指向的外鍵表),子屬性可能還有子屬性(多層的外鍵表),有些字段取值是集合而非單值(子表看作為主表的字段)。發(fā)生自關(guān)聯(lián)也不會影響理解(前面的例子就是個(gè)自關(guān)聯(lián)),同表有相同維度也不礙事(各自有各自的子屬性)。在這種關(guān)聯(lián)機(jī)制下,技術(shù)人員只要一次性把數(shù)據(jù)結(jié)構(gòu)(元數(shù)據(jù))定義好,在合適的界面下,由業(yè)務(wù)人員可以自己實(shí)現(xiàn)JOIN運(yùn)算,不再需要技術(shù)人員的參與。數(shù)據(jù)建模只發(fā)生于數(shù)據(jù)結(jié)構(gòu)改變的時(shí)刻,而不需要為新的關(guān)聯(lián)需求建模,這也就是非按需建模。