作者:京東物流 向往
一、背景
從事數據開發將近四年,過程中有大量任務交接或閱讀同事代碼的場景。在這些場景中發現有些SQL讀起來賞心悅目,可以一目了然地了解業務邏輯,一些復雜的業務需求實現方法也可以做到簡潔優雅,同時在性能上也有良好表現。而有些SQL讀起來非常艱難,時常要跨越幾百行尋找WHERE條件或者關聯字段,甚至充斥著大量相同的子查詢命名,除了作者可能少有人能快速看懂。
為此,基于個人經驗、理解與實踐,我總結了一些方法和技巧,能讓SQL盡量變得優雅,即兼顧代碼可讀性和執行性能兩方面的提升。
二、方法與技巧
1.子查詢與謂詞下推
很多同事在寫關聯邏輯時,習慣于直接將原表關聯,隨后在最下方用一大段WHERE語句進行條件過濾,如下示例:
// -------------------- Bad Codes ------------------------ SELECT f1.pin, c1.site_id, c2.site_name FROM fdm.fdm1 AS f1 LEFT JOIN cdm.cdm1 AS c1 ON f1.erp = lower(c1.account_number) LEFT JOIN cdm.cdm2 AS c2 ON c1.site_id = c2.site_code WHERE f1.start_date <= '""" + start_date + """' AND f1.end_date > '""" + start_date + """' AND f1.status = 1 AND c1.dt = '""" + start_date + """' AND c2.yn = 1 GROUP BY f1.pin, c1.site_id, c2.site_name
這段SQL主要有兩個問題:
1.cdm1和cdm2的條件寫在LEFT JOIN之后,因為cdm1和cdm2是NULL補充表(NULL 補充表: 右表被稱為 NULL 補充表,意味著它的存在是為了補充左表中可能缺失的值。即使在右表中沒有與左表匹配的行,左表中的行仍然會被返回,右表的相關列會填充為 NULL),那么19和20行無法進行謂詞下推,這會導致關聯時fdm1和cdm1,cdm2先進行全表關聯,再按照WHERE條件過濾分區。如果cdm1是每天全量的表,先關聯全表所掃描的數據量可想而知是相當大的。
2.全表關聯時沒有對關聯鍵進行NULL值處理,如果相關表的對應字段存在大量NULL值,會引起數據傾斜。
第一個問題涉及SQL的謂詞下推,即寫條件時,應該在不影響結果的情況下,盡量將過濾條件下推到join之前進行(“下推”指將條件推到靠近數據源的位置而不是SQL語句的方位)。謂詞下推后,過濾條件在map端執行,減少了map端的輸出,降低了數據在集群上傳輸的量,節約了集群的資源,也可以提升任務的性能。
對于常用的INNER JOIN和LEFT OUTER JOIN,謂詞下推規則如下:
INNER JOIN | LEFT OUTER JOIN | |||
---|---|---|---|---|
左表 | 右表 | 左表 | 右表 | |
ON條件 | 下推 | 下推 | 不下推 | 下推 |
WHERE條件 | 下推 | 下推 | 下推 | 不下推 |
如果使用上述示例的寫法,主要關注的是LEFT OUTER JOIN時WHERE語句里的條件是否會引起謂詞不下推。如果不想記這些看起來很復雜的規則怎么辦?可以如下所示直接使用子查詢:
// -------------------- Good Codes 審核編輯 黃宇
-
SQL
+關注
關注
1文章
773瀏覽量
44219 -
null
+關注
關注
0文章
19瀏覽量
3993
發布評論請先 登錄
相關推薦
評論