第9期:報(bào)表應(yīng)用的三層結(jié)構(gòu)
在傳統(tǒng)的報(bào)表應(yīng)用結(jié)構(gòu)中,報(bào)表工具一般都是與數(shù)據(jù)源直接連接,并沒(méi)有一個(gè)中間的數(shù)據(jù)計(jì)算層。確實(shí),大部分情況下的報(bào)表開(kāi)發(fā)并不需要這一層,相關(guān)的數(shù)據(jù)計(jì)算在數(shù)據(jù)源和呈現(xiàn)環(huán)節(jié)分別處理就夠了。不過(guò),在開(kāi)發(fā)過(guò)程中,我們發(fā)現(xiàn),有一部分報(bào)表的計(jì)算即不適合在數(shù)據(jù)源也不適合在呈現(xiàn)環(huán)節(jié)實(shí)現(xiàn),這類(lèi)報(bào)表在數(shù)量上并不占多數(shù),但耗用的開(kāi)發(fā)工作量占比卻很大。
有過(guò)程的計(jì)算
報(bào)表工具都可以完成計(jì)算列、分組排序等運(yùn)算,有些報(bào)表工具還提供了跨行組運(yùn)算和相對(duì)格與集合的引用方案,可以完成頗為復(fù)雜的運(yùn)算。
不過(guò),報(bào)表工具中的運(yùn)算是一種狀態(tài)式的計(jì)算,也就是把所有計(jì)算表達(dá)式寫(xiě)在報(bào)表布局中,根據(jù)依賴(lài)關(guān)系自動(dòng)處理計(jì)算次序。這種方法很直觀,在依賴(lài)關(guān)系不太復(fù)雜時(shí)能一目了然地了解各單元格的運(yùn)算目標(biāo)。但是,在依賴(lài)關(guān)系較為復(fù)雜,數(shù)據(jù)準(zhǔn)備計(jì)算需要分成多步時(shí),狀態(tài)式計(jì)算就困難了。如果一定要在報(bào)表中實(shí)施過(guò)程式計(jì)算,常常需要借用隱藏格,而隱藏格不僅將破壞狀態(tài)式運(yùn)算的直觀性,還會(huì)占用更多不必要的內(nèi)存。
比如要列出銷(xiāo)售額占前一半的大客戶(hù),如果不借助數(shù)據(jù)準(zhǔn)備環(huán)節(jié),就要在報(bào)表中使用隱藏行列手段將不該列出來(lái)的條目隱藏,而不能直接過(guò)濾掉。再比如帶明細(xì)的分組報(bào)表要按匯總值排序,需要先分組后排序,許多報(bào)表工具無(wú)法控制這個(gè)次序。
還有個(gè)典型例子是舍位平衡,明細(xì)值四舍五入后再合計(jì),可能會(huì)與合計(jì)值的四舍五入值不相等,會(huì)造成了報(bào)表上明細(xì)與合計(jì)數(shù)值不一致,需要根據(jù)合計(jì)的舍入值倒推明細(xì)的舍入值,這種計(jì)算的邏輯并不復(fù)雜,但即便用了隱藏格也難以由報(bào)表工具完成。
多樣性數(shù)據(jù)源
與多年前的單一數(shù)據(jù)源不同,現(xiàn)在有許多報(bào)表的數(shù)據(jù)源并不只來(lái)源于關(guān)系數(shù)據(jù)庫(kù),還可能是NoSQL數(shù)據(jù)庫(kù)、本地文件、從WEB上傳來(lái)的數(shù)據(jù)等。這些非關(guān)系數(shù)據(jù)庫(kù)的數(shù)據(jù)源缺乏標(biāo)準(zhǔn)的數(shù)據(jù)獲取接口和語(yǔ)法,有些甚至沒(méi)有最基本的過(guò)濾能力。而計(jì)算報(bào)表時(shí)總還要進(jìn)行一些過(guò)濾甚至關(guān)聯(lián)運(yùn)算,雖然報(bào)表工具一般都能提供這些計(jì)算能力,但由于都是內(nèi)存計(jì)算,只適合于數(shù)據(jù)量較小的情況,數(shù)據(jù)量較大時(shí)就會(huì)導(dǎo)致容量負(fù)擔(dān)過(guò)重。而且,大多數(shù)報(bào)表工具也不能很好地處理像json或XML這種多層數(shù)據(jù),也沒(méi)有靈活編碼能力以登錄遠(yuǎn)程WEB服務(wù)獲取數(shù)據(jù)。
動(dòng)態(tài)數(shù)據(jù)源也是常見(jiàn)的需求,報(bào)表工具使用的數(shù)據(jù)源一般事先配置好的,不能根據(jù)參數(shù)動(dòng)態(tài)選擇,直接使用報(bào)表工具無(wú)法實(shí)現(xiàn)。報(bào)表被用于通用查詢(xún)時(shí),取數(shù)用的SQL不能簡(jiǎn)單地用參數(shù)控制條件,而經(jīng)??赡芤鎿Q某個(gè)子句,有些報(bào)表工具支持宏替換,能夠一定程度地解決這個(gè)問(wèn)題,但根據(jù)參數(shù)計(jì)算宏值也是個(gè)有條件和過(guò)程的運(yùn)算,直接在報(bào)表工具中很難完成。
性能優(yōu)化問(wèn)題
我們?cè)谕诘奈恼轮性劦竭^(guò),大多數(shù)情況的報(bào)表性能問(wèn)題都需要在數(shù)據(jù)準(zhǔn)備階段來(lái)解決,其中有許多場(chǎng)景都不能在數(shù)據(jù)源內(nèi)部處理。比如并行取數(shù)本來(lái)就是解決數(shù)據(jù)源IO性能問(wèn)題,只能在數(shù)據(jù)源外部實(shí)現(xiàn);可控緩存需要在外存寫(xiě)入緩存信息,也不能在數(shù)據(jù)源內(nèi)部處理;清單列表中的異步數(shù)據(jù)緩存和按頁(yè)取數(shù)的功能,都不是數(shù)據(jù)源本身提供的能力;即使可以在數(shù)據(jù)源環(huán)節(jié)處理的多數(shù)據(jù)集關(guān)聯(lián)問(wèn)題,在多數(shù)據(jù)庫(kù)或非數(shù)據(jù)的場(chǎng)景、以及希望減輕數(shù)據(jù)庫(kù)負(fù)擔(dān)時(shí),仍然需要在數(shù)據(jù)源外部解決。這些無(wú)法在數(shù)據(jù)源內(nèi)部處理的場(chǎng)景,顯然也無(wú)法在報(bào)表環(huán)節(jié)處理。
數(shù)據(jù)計(jì)算層
如果把傳統(tǒng)報(bào)表應(yīng)用結(jié)構(gòu)的兩層改成三層,增加一個(gè)中間的數(shù)據(jù)計(jì)算層,這些問(wèn)題就容易解決了。
上述的各種運(yùn)算都可以在數(shù)據(jù)計(jì)算層實(shí)現(xiàn),報(bào)表工具只解決呈現(xiàn)問(wèn)題以及少量適合狀態(tài)式的直觀計(jì)算即可。
其實(shí),傳統(tǒng)報(bào)表應(yīng)用結(jié)構(gòu)雖然沒(méi)有刻意強(qiáng)調(diào)數(shù)據(jù)計(jì)算層,但仍然有這一層,只是比較隱蔽。典型的實(shí)現(xiàn)手段就是使用數(shù)據(jù)源中的存儲(chǔ)過(guò)程或者在應(yīng)用中使用報(bào)表工具的自定義數(shù)據(jù)源接口。存儲(chǔ)過(guò)程能夠解決一些過(guò)程式計(jì)算和性能優(yōu)化問(wèn)題,但它只能應(yīng)用于單個(gè)數(shù)據(jù)庫(kù)中,相當(dāng)于在數(shù)據(jù)源內(nèi)部的處理,對(duì)于必須在數(shù)據(jù)源外處理的場(chǎng)景無(wú)能為力,有較大的局限性。自定義數(shù)據(jù)源則在理論上可以解決上述所有問(wèn)題,而且?guī)缀跛袌?bào)表工具都提供有這個(gè)接口,所以這種方式的應(yīng)用更為廣泛。
那么,使用報(bào)表工具的自定義數(shù)據(jù)源是否就可以方便地實(shí)現(xiàn)數(shù)據(jù)計(jì)算層呢?我們將在下一期討論。