SQL研究-非平衡樹聚合

 

今天,我重新檢查了一個困擾已久的問題,在經過仔細的考慮,算是找到了一個比較合理的解決辦法。

 

問題是這樣的,表StoreProductSales記錄一系列的商場以及它們每天的營業額。商場與商場之間有從屬關係,記錄在Store_WithAcestor表裏。當用戶查詢某個商場的對某個產品的銷售額時,除了返回自己的營業額,還需要包含所有子商場的營業額。

 

示例結構和數據如下:

StoreProductSales

(

DayId     int,

StoreId  nvarchar(8),

ProductId nvarchar(8),

Sales int

)

Insert into StoreProductSales values(1,'BJ','Cloth',100)

Insert into StoreProductSales values(1,'BJ','Shoe',100)

Insert into StoreProductSales values(1,'BJ-HaiDian','Cloth',50)

 

Store_WithAncestor存放有

Parent, Child

BJ, BJ

BJ, BJ-HaiDian

BJ_HaiDian, BJ_Haidian

 

用戶輸入某個StoreID,開始DayId和結束DayId,輸出該Store在這段時間裏所有賣出的Product及其銷售量。

 

最簡單的方法是從Store_WithAncestor中選出需要store所有子store,如果用戶選擇BJ,那麼就把BJBJ-HaiDian都選出來,然後把所有這些商場上成交的產品銷量疊加起來;

 

這樣做是很慢的。不妨簡單的計算一下,如果某個商場有100個子商場,共計10000個商品,那麼爲了求出該商場的每個產品的日銷量,需要做1百萬次加法運算。同時應該考慮到數據庫的內存掃描工作。假設數據按照store做聚集索引,那麼對於每個商場,需要額外讀取1百萬的頁面。

 

怎樣才能使它快一點呢?

很容易想到的是如果我們能生成一個聚合表,在該表中存放的sales就是商場本身的銷售量加上所有孩子的銷售量。這樣查詢的時候,既不需要做online的計算,也不需要執行額外的IO操作。

 

但是這樣做有一個問題。

那就是您不能保證每個商品在每個商場每天都有銷量。

假設這樣的情形。在某天,clothBJ_haidian銷售了100,而在BJ 沒有銷售。那麼在上述的聚合表裏是不會出現BJ的記錄的,因爲它只存放自己和孩子的銷售量之和。當用戶查詢那天BJ的銷售額時,由於不查詢它的孩子,cloth的銷量是0

 

顯然這是一個錯誤。

 

當然如果原始表中有一條記錄,BJ cloth0,那一切都很好。但是你不可能那麼做。假如有1000store10000個商品,在表中每天加入1千萬行只是爲了佔位置,看起來是不可理喻的。

 

那麼還有沒有別的辦法呢?

我的方法是在源表的基礎上加一個列withParent,它表明在那一天的銷售記錄中,對於本產品是否有父輩的商場也出售了。例如,在20090601這一天,BJ_haidianbj都賣出了cloth,那麼這一行的withParent對應爲0,如果BJ沒有賣出,則爲1.

當用戶查詢BJ的銷售記錄時,我任然對它進行擴展,但我並不需要加載所有子節點對應的行,因爲withParent就在索引裏,我們只需要掃描索引節點就可以判斷是否需要加載這一行。因此我們不但可以減少加載所需的IO操作,還減少了額外的加法操作。

 

實際證明,該方法使得執行時間縮短了十分之一。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章