一?問(wèn)題背景
我們先來(lái)看一張資產(chǎn)負(fù)債表:
這是一個(gè)典型的中國(guó)式復(fù)雜報(bào)表格式厕倍,其復(fù)雜并不在于布局莽红,而在于其中“期末余額”的每個(gè)單元格都是一個(gè)需要獨(dú)立計(jì)算的指標(biāo)返弹,互相之間幾乎沒(méi)有關(guān)系凹联,事實(shí)上就是一個(gè)各種指標(biāo)的匯總清單成艘,而這些指標(biāo)往往會(huì)有上百個(gè)之多赏半。
在源數(shù)據(jù)表結(jié)構(gòu)中,有一個(gè)字段稱(chēng)為科目淆两,其長(zhǎng)度總是固定的 10 位断箫,如:1234567890,如下圖:
科目字段的值實(shí)際上是一個(gè)分層的代碼秋冰,而前面表里上百個(gè)指標(biāo)就是根據(jù)需求對(duì)不同層次科目數(shù)據(jù)的統(tǒng)計(jì)結(jié)果仲义,具體的做法是通過(guò)截取科目的前幾位來(lái)確定層次,然后按需求自由組合剑勾,作為條件進(jìn)行過(guò)濾埃撵,最后對(duì)金額字段進(jìn)行累計(jì)匯總。
比如計(jì)算指標(biāo) A 對(duì)應(yīng)的科目列表是 [1001虽另,1002]暂刘,代表累計(jì)所有前 4 位是 1001、1002 的科目捂刺,用 SQL 寫(xiě)出來(lái)就是:select sum(金額 )from T1 where concat( 年, 月)<=? and (left( 科目,4)="1001" or left(科目,4)="1002")
其中年谣拣、月是公共過(guò)濾條件,代表統(tǒng)計(jì)的時(shí)間范圍族展。
類(lèi)似的森缠,如果另一個(gè)指標(biāo) B 對(duì)應(yīng)的科目為 [2702,153102,12310105], 那就代表對(duì)前 4 位是 2702、前 6 位是 153102仪缸、前 8 位是 12310105 的所有科目值進(jìn)行累計(jì)贵涵,用 SQL 寫(xiě)出來(lái)就是:select sum(金額 )from T1 where concat( 年, 月)<=? and (left( 科目,4)="2702" or left(科目,6)="153102" or left(科目,8)="12310105");
實(shí)際業(yè)務(wù)中,每個(gè)指標(biāo)對(duì)應(yīng)的科目數(shù)量不定独悴,可能多達(dá) 10 個(gè)以上例书,而且就像指標(biāo) B 這樣,各個(gè)科目的層次也不盡相同刻炒。
在有了報(bào)表工具之后 (固定報(bào)表)决采,原則上這類(lèi)格式復(fù)雜、指標(biāo)參數(shù)任意組合的報(bào)表需求并不難實(shí)現(xiàn)坟奥,只是原始數(shù)據(jù)量一大树瞭,查詢(xún)響應(yīng)就會(huì)非常慢,用戶體驗(yàn)變差爱谁,當(dāng)多并發(fā)請(qǐng)求時(shí)晒喷,還會(huì)對(duì)正常業(yè)務(wù)產(chǎn)生影響。
二?開(kāi)發(fā)和優(yōu)化過(guò)程
2.1 多次遍歷方案
最常見(jiàn)的開(kāi)發(fā)思路访敌,就是按前面說(shuō)的計(jì)算方式凉敲,對(duì)報(bào)表的每個(gè)指標(biāo)都寫(xiě)一句完整的 SQL 來(lái)計(jì)算,有 100 個(gè)指標(biāo)寺旺,就寫(xiě) 100 個(gè) SQL爷抓。
有些報(bào)表工具提供了函數(shù),可以直接在單元格中執(zhí)行 SQL (比如 query/call 等),單元格的表達(dá)式大概會(huì)是這樣:
= query("select sum(金額 )from T1 where concat( 年, 月)<=? and (left( 科目,4)='2702' or left(科目,6)='153102' or left(科目,8)='12310105')", concat(year,month))
如果是非多源報(bào)表工具,則可以借助外部程序數(shù)據(jù)源來(lái)實(shí)現(xiàn)牺六,比如可以直接用集算器編寫(xiě)以下腳本:
簡(jiǎn)單說(shuō)明一下:
A1:連接數(shù)據(jù)庫(kù) demo
A2-A3:執(zhí)行指標(biāo) A 的查詢(xún) SQL 和指標(biāo) B 的查詢(xún) SQL;其中 query() 函數(shù)中 @1 選項(xiàng)代表查詢(xún)符合條件的第一條記錄, 返回成單值或序列(一個(gè)字段是單值渤昌,多個(gè)字段是序列);這個(gè)例子是對(duì)金額匯總求和走搁,所以返回單值独柑。
A4-A101:假定有剩余的 98 個(gè)指標(biāo),每個(gè)指標(biāo)的查詢(xún) SQL 都類(lèi)似于 A2朱盐、A3 的寫(xiě)法
A102:關(guān)閉數(shù)據(jù)庫(kù)連接
A103:合并 A2-A101 每個(gè)格子的計(jì)算結(jié)果 (共計(jì) 100 個(gè)指標(biāo)值)群嗤,返回一個(gè)單列數(shù)據(jù)集,供報(bào)表工具使用兵琳。
不過(guò),在這種思路下骇径,無(wú)論直接在格中使用 SQL 還是在程序數(shù)據(jù)源中計(jì)算躯肌,實(shí)際上每計(jì)算一個(gè)指標(biāo)就得遍歷一次源數(shù)據(jù);而每個(gè)指標(biāo)還對(duì)應(yīng)多個(gè)需要 AND 的條件破衔,這些都會(huì)嚴(yán)重降低性能清女。
這種思路的優(yōu)點(diǎn)是簡(jiǎn)單直接,看上去確實(shí)能夠?qū)崿F(xiàn)需求晰筛,開(kāi)發(fā)過(guò)程也并不太難嫡丙。在數(shù)據(jù)量不大的情況下拴袭,查詢(xún)也不會(huì)很慢,勉強(qiáng)還能接受曙博。不過(guò)拥刻,隨著數(shù)據(jù)量越來(lái)越大,性能瓶頸就會(huì)隨之而來(lái)父泳,到了一定程度后般哼,就可能出現(xiàn)在關(guān)鍵時(shí)刻用戶無(wú)法及時(shí)獲得自己想要指標(biāo)的問(wèn)題,最終只能放棄惠窄。
2.2 一次遍歷方案
上面“多次遍歷方案”的問(wèn)題在于蒸眠,無(wú)論如何,對(duì)源數(shù)據(jù)遍歷 100 次實(shí)在是太低效了杆融,那么楞卡,我們有沒(méi)有辦法能少遍歷幾次呢? 能不能只做一次遍歷就把所有指標(biāo)都計(jì)算出來(lái)呢?
這種一次遍歷的思路確實(shí)是可以的脾歇,我們只需要把 SQL 中的 WHERE 條件拼到 SELECT 中就行了蒋腮,比如前面說(shuō)到的指標(biāo) A 和 B 可以寫(xiě)成:
SELECT SUM(CASE WHEN (LEFT(科目,4)='1001' OR LEFT( 科目,4)='1002')THEN 金額 ELSE 0 END) 指標(biāo) A,
SUM(CASE WHEN (LEFT(科目,4)="2702" OR LEFT( 科目,6)="153102" OR LEFT(科目,8)="12310105")THEN 金額 ELSE 0 END) 指標(biāo) B,
??? …
FROM T1 WHERE CONCAT(年, 月 )<=?
但是,真要用這個(gè)思路來(lái)處理 100 個(gè)指標(biāo)介劫,可以想見(jiàn)這個(gè) SQL 會(huì)有多長(zhǎng)徽惋,維護(hù)難度會(huì)有多大。為此座韵,我們可以利用集算器的游標(biāo)來(lái)實(shí)現(xiàn)這個(gè)邏輯险绘,適當(dāng)降低維護(hù)難度。
另外誉碴,這種方案下的遍歷宦棺,還是需要把整表數(shù)據(jù)讀出數(shù)據(jù)庫(kù),而 JDBC 太慢黔帕,IO 時(shí)間很可能成為瓶頸代咸。對(duì)于這個(gè)問(wèn)題,我們注意到其中處理的都是不再變化的歷史數(shù)據(jù)成黄,那么我們就可以把數(shù)據(jù)先搬出數(shù)據(jù)庫(kù)存成文件呐芥,然后用文件作為數(shù)據(jù)源,從而加快 IO 訪問(wèn)奋岁。具體實(shí)現(xiàn)如下:
1思瘟、把數(shù)據(jù)搬出數(shù)據(jù)庫(kù)保存成文件,集算器的 SPL 腳本如下:
A1:連接數(shù)據(jù)庫(kù) demo
A2:根據(jù) sql 創(chuàng)建數(shù)據(jù)庫(kù)游標(biāo)返回
A3:集文件保存的位置
A4:導(dǎo)出整表數(shù)據(jù)并保存到文件中闻伶,其中 export()函數(shù)的 @b 選項(xiàng)代表寫(xiě)入到集文件中滨攻,即總賬憑證 -pre.btx
A5:關(guān)閉數(shù)據(jù)庫(kù)連接
2、遍歷一次源數(shù)據(jù),計(jì)算 100 個(gè)指標(biāo)光绕,集算器的 SPL 腳本如下:
值得注意的是:這個(gè)例子引入了一個(gè)新的寫(xiě)法女嘲,100 個(gè)指標(biāo)參數(shù)可以統(tǒng)一寫(xiě)到 C 列上诞帐,當(dāng) B 列每計(jì)算一個(gè)指標(biāo)時(shí)欣尼,直接引用 C 列當(dāng)前行的所對(duì)應(yīng)的參數(shù)即可。比如:
C5:指標(biāo) A 的參數(shù)條件 (按科目號(hào)前 4 位截取的多個(gè)值形成的集合)
C6:指標(biāo) B 的參數(shù)條件 (按科目號(hào)前 4 位 / 前 6 位 / 前 8 位截取的多個(gè)值景埃,形成的參數(shù)集合)
剩余的 98 個(gè)指標(biāo)媒至,計(jì)算的寫(xiě)法類(lèi)似 B6,參數(shù)的寫(xiě)法類(lèi)似 C6谷徙,依次類(lèi)推到 100拒啰。
顯然,這種計(jì)算邏輯和參數(shù)分離的寫(xiě)法完慧,能夠極大地提高可維護(hù)性谋旦。
下面我們完整地分析一下這段腳本:
A1:打開(kāi)預(yù)處理前的原始數(shù)據(jù)表的集文件對(duì)象
A2:根據(jù)文件創(chuàng)建游標(biāo)返回,其中 cursor() 函數(shù)使用 @b 選項(xiàng)代表從集文件中讀取屈尼。
A3:在 A2 的基礎(chǔ)上册着,先按公共條件年、月過(guò)濾出結(jié)果集中符合條件的記錄脾歧,其中 year,month 是 SPL 腳本中定義的參數(shù)甲捏,接收來(lái)自報(bào)表前端傳入的查詢(xún)條件,比如查詢(xún) 2017 年 01 月鞭执,日期范圍是截止到某個(gè)時(shí)間點(diǎn)司顿,所以需要利用 concat 函數(shù)對(duì) year、month 連接起來(lái)再去做條件比較兄纺。
A4:循環(huán)游標(biāo)大溜,每次從游標(biāo)讀取 10000 條記錄返回。
B5:代表指標(biāo) A 的金額累計(jì)匯總值估脆;每次 for 循環(huán)钦奋,根據(jù) C5 的參數(shù)選出符合條件的記錄,用 contain()函數(shù)來(lái)判斷參數(shù)是否在結(jié)果集中 ( 其中參數(shù)都是 4 位疙赠,所以需要對(duì)原記錄中科目 \1000000 后保留科目的前 4 位付材,才能與參數(shù)進(jìn)行比較),然后對(duì)金額進(jìn)行累計(jì)匯總圃阳。其中的 @符號(hào)代表當(dāng)前格的值伞租,初始值為空,每次循環(huán)時(shí)將上次的值與本次符合條件的數(shù)據(jù)值相加限佩,作為新值寫(xiě)入格中,最終可計(jì)算出某個(gè)指標(biāo)的金額累計(jì)匯總。
B6:代表指標(biāo) B 的金額累計(jì)匯總值祟同;與指標(biāo) A 不同的是作喘,多個(gè)參數(shù)由不同的位數(shù)組成,所以需要在 contain()函數(shù)中分別截取不同的位數(shù)晕城,與 C6 列的參數(shù)進(jìn)行多次比較泞坦。
A105:合并 B5-B104 每個(gè)格子的值 (從上往下,100 個(gè)指標(biāo)的計(jì)算結(jié)果)砖顷,返回一個(gè)單列數(shù)據(jù)集贰锁,可以供報(bào)表工具使用。
2.3 預(yù)先匯總方案
現(xiàn)在我們已經(jīng)做到了只需要遍歷一次數(shù)據(jù)滤蝠,但需要遍歷的整體數(shù)據(jù)量仍然比較大豌熄,還有什么辦法能進(jìn)一步減少數(shù)據(jù)量呢?
如果能夠把數(shù)據(jù)事先按科目匯總物咳,那么我們就可以不必重復(fù)累加科目相等的記錄了锣险,而且存儲(chǔ)量也會(huì)變少,IO 也會(huì)更快览闰。
2.3.1 分組計(jì)算匯總值
首先芯肤,按照科目、年压鉴、月分組崖咨,金額進(jìn)行匯總,匯總結(jié)果的數(shù)據(jù)結(jié)構(gòu)應(yīng)當(dāng)是:科目油吭、年击蹲、月、本科目下當(dāng)月的金額匯總值上鞠。
集算器 SPL 腳本實(shí)現(xiàn)分組际邻、匯總計(jì)算的樣例如下:
A1:打開(kāi)預(yù)處理前的原始數(shù)據(jù)表的集文件對(duì)象
A2:計(jì)算后中間結(jié)果數(shù)據(jù)的集文件保存的位置
A3:根據(jù)文件創(chuàng)建游標(biāo)返回,其中 cursor() 函數(shù)的 @b 選項(xiàng)代表從集文件中讀取
A4:先按照科目芍阎、年世曾、月分組,金額匯總
A5:執(zhí)行 A4 的計(jì)算結(jié)果寫(xiě)入到集文件中谴咸,其中 export() 函數(shù)使用了 @b 的選項(xiàng)轮听,@b 代表寫(xiě)成集文件格式,即總賬憑證 -mid.btx
2.3.2 利用跨行組計(jì)算累計(jì)值
我們這個(gè)問(wèn)題最終是要計(jì)算指標(biāo)的期末值岭佳,也就是截止某個(gè)日期的金額累計(jì)值血巍;上一步計(jì)算的是當(dāng)月的金額匯總值,那金額的累計(jì)值該如何計(jì)算呢珊随?
集算器提供了跨行引用的語(yǔ)法述寡,可以用A[-1]代表上一行的 A柿隙,這樣就可以計(jì)算:累計(jì)值 = 上一行的累計(jì)值 + 當(dāng)前行值。
腳本中鲫凶,接著上一步作如下修改即可計(jì)算累計(jì)值:
其他格子的代碼禀崖,在上面已經(jīng)解釋過(guò)了,這里不再贅述螟炫。
A5:利用 for 循環(huán)游標(biāo) A4波附,其中分號(hào)的參數(shù)“科目”表示每次從游標(biāo)讀取一組科目值相同的記錄返回。我們先單步執(zhí)行一下昼钻,返回某一個(gè)科目的記錄:
再接著執(zhí)行一次 for 循環(huán)掸屡,返回下一組科目的記錄:
B5:針對(duì)取出的同一科目的記錄,對(duì)金額累計(jì)然评;其中表達(dá)式:金額 = 金額 [-1]+ 金額仅财,金額代表當(dāng)前行金額,金額[-1] 代表上一行累計(jì)金額值沾瓦,相加計(jì)算好后再重新賦值給金額字段满着。如下圖是接著 A5 格子執(zhí)行后的結(jié)果變化:
B6:執(zhí)行計(jì)算后的結(jié)果寫(xiě)入到集文件中。其中 export() 函數(shù)使用了 @ab 的選項(xiàng)贯莺,@b 代表寫(xiě)成集文件格式风喇,由于在 for 循環(huán)里面,需要執(zhí)行多次缕探,所以用 @a 以追加的方式把結(jié)果逐步保存到文件中魂莫,保證文件的完整性;即總賬憑證 -mid.btx爹耗。部分執(zhí)行結(jié)果如下圖:
2.3.3 構(gòu)造多層科目匯總值
現(xiàn)在計(jì)算出了明細(xì)科目的累計(jì)值耙考,我們還需要計(jì)算高層次科目(截取前 N 位)對(duì)應(yīng)的匯總值。
從需求可以看到潭兽,每個(gè)計(jì)算指標(biāo)都是按照科目截取前 4 位倦始、前 6 位、前 8 位等作為參數(shù)集合山卦,那么在構(gòu)造不同層次的科目號(hào)時(shí)鞋邑,也需要和這種規(guī)則匹配,從而計(jì)算出不同層次的聚合值账蓉。
比如:對(duì)于科目是 1234567890枚碗,那么就需要新增科目號(hào) 1234、123456铸本、12345678 對(duì)應(yīng)的匯總金額肮雨。也就是對(duì)于每個(gè) 1234567890 這樣的 10 位科目號(hào),還需要分別增加 4箱玷、6怨规、8 位的科目 1234陌宿、123456、12345678椅亚。其中科目 1234 會(huì)把所有 1234 開(kāi)頭的科目的金額值進(jìn)行累計(jì)匯總限番,依次類(lèi)推。其實(shí)呀舔,這就是 CUBE 的常用手段。
需要注意的是扩灯,基于上一步計(jì)算結(jié)果媚赖,數(shù)據(jù)量大小又需要分兩種情況討論:
1、 結(jié)果已經(jīng)可以全部直接讀入內(nèi)存參與下一步計(jì)算珠插;
2惧磺、 結(jié)果依然很大,需要采用外存計(jì)算 (游標(biāo)技術(shù)可以邊讀邊算捻撑,多次計(jì)算還需要管道技術(shù)來(lái)配合)
2.3.3.1 內(nèi)存計(jì)算
如果結(jié)果集可以全部裝入內(nèi)存磨隘,集算器 SPL 腳本構(gòu)造多層次科目匯總值的樣例如下:
A1:打開(kāi)中間計(jì)算結(jié)果的集文件對(duì)象
A2:計(jì)算后的結(jié)果集文件保存的位置
A3:從文件對(duì)象 A1 中讀出內(nèi)容作為記錄形成結(jié)果集返回;其中 @b 代表從集文件中讀出顾患。
A4:按科目截取前 8 位 (科目 \100)番捂、年、月進(jìn)行分組江解,累計(jì)金額進(jìn)行匯總设预,如果截取前 7 位,就需要寫(xiě)成:(科目 \1000)犁河;具體按多少位截取由需求場(chǎng)景決定鳖枕。執(zhí)行結(jié)果如下圖:
A5:在 A4 的結(jié)果集的基礎(chǔ)上,按科目 \100 得到科目前 6 位桨螺、年宾符、月進(jìn)行分組,累計(jì)金額進(jìn)行匯總灭翔;執(zhí)行結(jié)果如下圖:
A6:同理魏烫,在 A5 的基礎(chǔ)上,按科目 \100 得到科目前 4 位缠局、年则奥、月進(jìn)行分組,累計(jì)金額進(jìn)行匯總狭园;執(zhí)行結(jié)果如下圖:
A7:多個(gè)結(jié)果集合并成一個(gè)結(jié)果集
A8:計(jì)算后的結(jié)果集導(dǎo)出并保存到文件中读处,其中 export()函數(shù)使用了 @z 的選項(xiàng),代表分段寫(xiě)入到集文件中唱矛,即總賬憑證 -later.btx
2.3.3.2 外存計(jì)算(游標(biāo) + 管道)
在前面的例子中罚舱,我們已經(jīng)使用了游標(biāo)井辜,需要特別強(qiáng)調(diào)的是游標(biāo)只能從前向后單向移動(dòng),執(zhí)行一次遍歷計(jì)算管闷,只有最終生成的游標(biāo)中的 cs.fetch() 函數(shù)才能夠有效取得數(shù)據(jù)粥脚。遍歷結(jié)束后,計(jì)算過(guò)程中產(chǎn)生的其它游標(biāo)都將不能再次讀取數(shù)據(jù)包个。
不過(guò)有時(shí)候刷允,在一次讀取數(shù)據(jù)的過(guò)程中,我們需要同時(shí)計(jì)算出多個(gè)結(jié)果碧囊,那么此時(shí)就需要使用與游標(biāo)類(lèi)似的管道树灶,用 channel(cs) 建立管道將游標(biāo) cs 的數(shù)據(jù)在遍歷同時(shí)壓入管道以便實(shí)施其它運(yùn)算。
和內(nèi)存相比糯而,外存速度慢很多天通,因此要盡量減少硬盤(pán)訪問(wèn),所以熄驼,我們采用游標(biāo) + 管道的機(jī)制一次遍歷獲得需要的匯總結(jié)果:
A1-A3:前面已經(jīng)解釋過(guò)了像寒,這里不再贅述。
A4:創(chuàng)建管道瓜贾,將游標(biāo) A3 中的數(shù)據(jù)推送到管道诺祸,其中 ch.groupx() 函數(shù)針對(duì)管道中的有序記錄分組并返回管道;按科目截取前 8 位阐虚、年序臂、月進(jìn)行分組,累計(jì)金額進(jìn)行匯總
A5:同理于 A4 返回管道实束,按科目截取前 6 位奥秆、年、月進(jìn)行分組咸灿,累計(jì)金額進(jìn)行匯總
A6:返回游標(biāo)构订,按科目截取前 4 位、年避矢、月進(jìn)行分組悼瘾,累計(jì)金額進(jìn)行匯總
A7:多個(gè)游標(biāo)運(yùn)算結(jié)果合并成一個(gè)結(jié)果集;其中 ch.result() 代表管道的運(yùn)算結(jié)果
A8:計(jì)算后的結(jié)果集導(dǎo)出并保存到集文件审胸,即總賬憑證 -later.btx
2.3.4 優(yōu)化“一次遍歷”的方案
經(jīng)過(guò)上面兩步數(shù)據(jù)預(yù)處理亥宿,結(jié)果數(shù)據(jù)可以直接作為報(bào)表的數(shù)據(jù)源,每個(gè)指標(biāo)的計(jì)算條件只要相等比較就可以砂沛,而不再需要截取烫扼、計(jì)算前幾位了。
所以在前述“一次遍歷“方案的基礎(chǔ)上碍庵,我們來(lái)做一些優(yōu)化映企;集算器的 SPL 腳本樣例如下:
A1-A4:前面已經(jīng)解釋過(guò)了悟狱,這里不再贅述。
B5:代表指標(biāo) A 的累計(jì)金額的匯總值求和堰氓;每次 for 循環(huán)挤渐,根據(jù) C5 的參數(shù)選出符合條件的記錄,用 contain() 函數(shù)來(lái)判斷參數(shù)是否在結(jié)果集中双絮,然后對(duì)累計(jì)金額匯總進(jìn)行求和浴麻。其中的 @符號(hào)代表當(dāng)前格的值,初始值為空掷邦,每次循環(huán)時(shí)將上次的值與本次符合條件的數(shù)據(jù)值相加白胀,作為新值寫(xiě)入格中,最終可計(jì)算出某個(gè)指標(biāo)的累計(jì)金額匯總的求和值抚岗。
B6:同 B5 的寫(xiě)法,代表指標(biāo) B 的累計(jì)金額匯總值求和哪怔,通常集合元素個(gè)數(shù)超過(guò) 13 個(gè)時(shí)宣蔚,如果事先能對(duì)常數(shù)集合排序,那么可以選擇 contain() 函數(shù)的 @b 選項(xiàng)认境,利用二分查找會(huì)明顯快于順序查找胚委。
A105:合并 B5-B104 每個(gè)格子的值 (從上往下,100 個(gè)指標(biāo)的計(jì)算結(jié)果)叉信,返回一個(gè)單列數(shù)據(jù)集亩冬,供報(bào)表工具使用。
至此硼身,我們可以看到硅急,按照預(yù)先匯總的思路,事先根據(jù)數(shù)據(jù)特征對(duì)數(shù)據(jù)進(jìn)行預(yù)處理佳遂,可以讓總的數(shù)據(jù)量變小营袜,同時(shí)減少遍歷量,從而避免前述方案中總是從最底層再去累加的模式丑罪。經(jīng)過(guò)實(shí)測(cè):從報(bào)表取數(shù)到報(bào)表展現(xiàn)整個(gè)環(huán)節(jié)比“常規(guī)”方案足足提高了6-8倍左右荚板,這樣的體驗(yàn)已經(jīng)可以很好地滿足用戶要求了。
那么吩屹,是否有更好的優(yōu)化方案呢跪另?答案是肯定的!請(qǐng)看:多層科目任意組合匯總報(bào)表的性能優(yōu)化 (下)