優化Join運算的系列方法(3)

5 綜合案例

實際業務中,多表關聯運算十分常見,外鍵表、同維表、主子表這幾種關聯類型可能會混合出現。下面咱們來看一個綜合案例。性能

5.1 表結構和查詢目標

某電商平臺中和訂單編號這個字段相關的有6個表,主要表結構以下:fetch

訂單表 訂單明細表 訂單優惠表 訂單發貨表 訂單支付表 訂單評價表
訂單編號 訂單編號 訂單編號 訂單編號 訂單編號 訂單編號
用戶編號 商品編號 優惠類型 快遞編碼 支付渠道 評分
賣家編號 數量 優惠金額 支付時間 評論時間
下單時間 金額 是否分期 評價
訂單狀態
1:N 1:N 1:1 1:1 1:1

他們都靠訂單編號字段進行關聯,下面是訂單表和另外5個表的對應關係:優化

用戶信息表 用戶地址信息表
用戶編號 用戶編號
用戶名
手機
註冊時間 區縣
VIP級別 地址
1:1

用戶表和用戶地址表,這兩個表是按照用戶編號字段1對1的關係,這是同維表狀況。編碼

商品信息表 類別信息表
商品編號 類別編號
名稱 大類名稱
類別編號 二級名稱
廠商編號
1:1

商品信息表和類別信息表是經過類別編碼進行關聯,這是外鍵表的狀況。spa

賣家信息表
賣家編號
開戶行
聯繫
名稱
信用級

最後還有一個賣家信息表。這裏一共有11個表,假設要作這樣一個查詢:如今想知道江浙滬三省的VIP用戶在6月分內從5星級賣家那裏購買的全部電腦類商品的詳情,而且要求只統計那些優惠總金額大於100元、用戶評分4分以上的使用郵政配送的訂單,並且這些訂單不能是分期付款的。code

使用SQL實現:排序

SELECT \* FROM

 訂單表,

     (SELECT用戶編號 FROM 用戶信息表 用戶,用戶地址信息表 地址 WHERE 用戶.用戶編號=地址.用戶編號 AND 用戶.VIP級別>0 AND (地址.省=江蘇 OR 地址.省=浙江 OR 地址.省=上海) ) 用戶,

     (SELECT賣家編號 FROM 賣家信息表 WHERE 賣家信息表.等級=5) 賣家,

     (SELECT訂單編號 FROM 訂單優惠表 GROUP BY 訂單編號 HAVING SUM(優惠金額)>100) 優惠,

     (SELECT訂單編號 FROM 訂單發貨表 WHERE 快遞編碼=1) 快遞,

     (SELECT訂單編號 FROM 訂單支付表 WHERE 是否分期=false) 支付,

     (SELECT訂單編號 FROM 訂單評價表 WHERE 評分>=4) 評價,

     (SELECT訂單編號 FROM 訂單明細表,(SELECT \* FROM 商品信息表 JOIN 類別信息表 ON 商品信息表.類別編號=類別信息表.類別編號 WHERE 大類名稱='電腦') WHERE 訂單明細表.商品編號=商品信息表.商品編號) 明細

WHERE

訂單表.用戶編號=用戶.用戶編號

AND 訂單表.賣家編號=賣家.賣家編號

AND 訂單表.訂單編號=優惠.訂單編號

AND 訂單表.訂單編號=快遞.訂單編號

AND 訂單表.訂單編號=支付.訂單編號

AND 訂單表.訂單編號=評價.訂單編號

AND 訂單表.訂單編號=明細.訂單編號

5.2 分析關聯類型

這個SQL看上去彷佛很清楚,理解起來也不難,可是性能卻可能慘不忍睹。爲了優化查詢的性能,咱們須要先對這個查詢進行拆分,獲得如下幾個子步驟:圖片

P1=對用戶表、用戶地址表關聯,獲得江浙滬三省VIP用戶的用戶編號,這是同維表狀況;內存

P2=賣家信息表取遊標,條件是信用級別=5,獲得賣家編碼;同步

P3=對訂單優惠表按照訂單編號分組,按條件(優惠總金額>=100元)過濾;

P4=訂單發貨表取遊標,條件是快遞編碼=1(郵政快遞編碼);

P5=訂單支付表取遊標,條件是是否分期=false;

P6=訂單評價表取遊標,條件是評分>=4;

P7=商品信息表和類別信息表用類別編碼作外鍵關聯,用條件(大類=電腦)過濾;

P8=訂單明細表經過商品編號字段對P7作外鍵關聯;

P9=訂單表依次對P一、P2作外鍵關聯;

這時P三、P四、P五、P六、P八、P9這幾個子查詢都是同維或者主子表的關係,對它們經過訂單編碼字段作有序歸併,就獲得了須要查詢的結果。

5.3 數據預處理

獲得關聯類型後就能夠有針對性地進行預處理。

首先,對無序的同維表、主子表進行排序處理,好比訂單發貨表、訂單支付表、訂單評價表一般是無序的,就要先對這些表進行排序;

第二步,還能夠對外鍵表進行外鍵序號化,好比賣家信息表是訂單表的外鍵表,就能夠外鍵序號化。

5.4 查詢實現

用戶信息表很大,但查詢目標是VIP級別的用戶,符合VIP這個條件的用戶並很少,進行過濾後就能夠裝入內存,因此P1子查詢能夠所有裝入內存;一樣,用戶地址信息表做爲用戶信息表的同維表也很大,但屬於江浙滬三省的用戶並很少,通過過濾後能夠所有導入內存。把這兩個同維表關聯後,而後再完成訂單表的關聯計算,來看看這個子查詢的寫法:

圖片描述

A1,獲得用戶信息表的遊標,並按條件過濾;

A2,獲得用戶地址信息表的遊標,並按條件過濾;

A3,對A一、A2按照用戶編號字段進行有序歸併,返回的結果只取用戶編號;

A4,獲得訂單表的遊標,並按條件過濾;

A5,把A4和A3作外鍵關聯;

A6,返回結果只取訂單編號、賣家編號和用戶編號字段;

A7,返回執行結果;

把這個腳本保存爲P9.dfx。

接下來實現商品信息表和類別信息表的關聯。類別信息表是商品信息表的外鍵,這個表很大沒法裝入內存。可是大類是電腦的類別信息就很少了,因此用大類等於電腦這個條件先過濾一下後就能夠裝入內存。下面是個子查詢,把大類是電腦的全部商品的編碼所有導入內存:

A
1 =file("類別信息表").import@b().select(大類名稱=="電腦")
2 =file("商品信息表").cursor@b().select(VIP級別>=1)
3 =A2.switch@i(商品編號,A1:商品編號)
4 =A3.new(商品編號).fetch()
5 return A4

A1,獲得類別信息表的數據,並按條件過濾後取出;

A2,獲得商品信息表遊標,並按條件過濾;

A3,把A2的商品編號字段替換爲A1的對應記錄;

A4,結果只取商品編號字段;

A5,返回執行結果;

這個腳本保存爲P7.dfx。

上面是兩個子查詢的處理,整個查詢的實現是這樣:

A
1 =file("訂單優惠表").cursor@b().group(訂單編號;sum(優惠金額):優惠總額).select(優惠總額>=100)
2 =file("訂單發貨表").cursor@b().select(快遞編碼==1)
3 =file("訂單支付表").cursor@b().select(是否分期==false)
4 =file("訂單評價表").cursor@b().select(評分>=4)
5 =file("賣家信息表").import@b()
6 =call("P9.dfx")
7 =A6.switch@i(賣家編號,A5:#).select(賣家編號.信用級==5).sort(訂單編號)
8 =call("P7.dfx")
9 =file("訂單明細表").cursor@b()
10 =A9.switch@i(商品編號,A8:商品編號)
11 =joinx(A7:訂單,訂單編號; A10:明細,訂單編號; A1,訂單編號;A2,訂單編號;A3,訂單編號;A4,訂單編號)

A1,獲得訂單優惠表遊標;

A2,獲得訂單發貨表遊標;

A3,獲得訂單支付表遊標;

A4,獲得訂單評價表遊標;

A5,獲得賣家信息表數據(這裏認爲賣家信息表的數據能夠導入內存);

A6,調用P9.dfx;

A7,把A6的結果的賣家編號替換成賣家信息表裏的對應記錄,按條件(信用級=5)進行過濾,並排序;

A8,調用P7.dfx,獲得大類是電腦的全部商品的編碼;

A9,獲得訂單明細表遊標;

A10,把訂單明細表的商品編號替換成A8結果裏的對應記錄;

A11,對A七、A十、A一、A二、A三、A4進行有序歸併;

5.5 查詢技巧

技巧一:若是維表在內存中放不下,先彆着急,能夠看看總的查詢條件裏是否對這個維表進行了過濾。若是有,那麼就能夠把條件提取出來對維表進行過濾,不少時候過濾以後的結果就能夠裝入內存了。

技巧二:若是維表能夠裝入內存,而且已經外鍵序號化,那麼就不要先過濾維表。由於能裝入內存時用序號化作外鍵關聯是最快的。例子中就是對賣家信息表先作關聯,而後再進行條件過濾。

總結

使用集算器解決 JOIN 運算性能問題時,能夠按照這個流程來處理:首先判斷 JOIN 運算類型;若是是外鍵表,就裝入內存並作外鍵序號化,若是沒法裝入內存也要儘可能先用條件過濾,有條件的儘可能作外鍵序號化;若是是同維表或主子表,要判斷是否有序,有序則能夠直接作有序歸併,若是無序的則要先進行排序。同時,若是幾個表是同步分段的還能夠經過並行來提升性能。

相關文章
相關標籤/搜索