實際業務中,多表關聯運算十分常見,外鍵表、同維表、主子表這幾種關聯類型可能會混合出現。下面咱們來看一個綜合案例。性能
某電商平臺中和訂單編號這個字段相關的有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 訂單表.訂單編號=明細.訂單編號
這個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這幾個子查詢都是同維或者主子表的關係,對它們經過訂單編碼字段作有序歸併,就獲得了須要查詢的結果。
獲得關聯類型後就能夠有針對性地進行預處理。
首先,對無序的同維表、主子表進行排序處理,好比訂單發貨表、訂單支付表、訂單評價表一般是無序的,就要先對這些表進行排序;
第二步,還能夠對外鍵表進行外鍵序號化,好比賣家信息表是訂單表的外鍵表,就能夠外鍵序號化。
用戶信息表很大,但查詢目標是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進行有序歸併;
技巧一:若是維表在內存中放不下,先彆着急,能夠看看總的查詢條件裏是否對這個維表進行了過濾。若是有,那麼就能夠把條件提取出來對維表進行過濾,不少時候過濾以後的結果就能夠裝入內存了。
技巧二:若是維表能夠裝入內存,而且已經外鍵序號化,那麼就不要先過濾維表。由於能裝入內存時用序號化作外鍵關聯是最快的。例子中就是對賣家信息表先作關聯,而後再進行條件過濾。
使用集算器解決 JOIN 運算性能問題時,能夠按照這個流程來處理:首先判斷 JOIN 運算類型;若是是外鍵表,就裝入內存並作外鍵序號化,若是沒法裝入內存也要儘可能先用條件過濾,有條件的儘可能作外鍵序號化;若是是同維表或主子表,要判斷是否有序,有序則能夠直接作有序歸併,若是無序的則要先進行排序。同時,若是幾個表是同步分段的還能夠經過並行來提升性能。