sql server 數據分析優化實戰(一)——SQL語句優化


前言

在我們進行數據分析的時候,首要的目標是根據業務邏輯,通過編寫SQL代碼得到我們想要的結果,這是毋庸置疑的。一般情況下,由於我們分析的數據量比較少,體會不出SQL語句各種寫法的性能優劣,對SQL代碼的優化往往沒那么重要。但是隨着數據庫中數據的增加,尤其是當一個系統需要對海量的數據進行持續性的分析時,SQL的運行效率就成為系統需要解決的最主要的問題之一。系統優化中一個很重要的方面就是SQL語句的優化。對於海量數據,劣質SQL語句和優質SQL語句之間的速度差別可以達到上百倍甚至更多,可見對於數據分析,不是簡單地能實現其功能就可,而是要寫出高質量的SQL語句,提高系統的可用性和效率。接下來,將介紹幾種常用的SQL優化技巧。

WHERE下多個過濾條件的排列順序

我們在使用WHERE關鍵字進行條件篩選時,經常會包含多個篩選條件,使用AND進行連接。數據庫在對WHERE關鍵字進行解析時,采用自下而上的順序進行,即從最后一個過濾條件向前解析。根據這個原理,那些可以過濾掉最大數量記錄的條件應該寫在WHERE子句的末尾,以減少數據庫需要遍歷的數據量。例如,我們要從全網3天的KPI數據中篩選出一個站一天的數據。顯然,先篩選基站能盡快濾掉更多的數據。因此,我們應把基站篩選放在后面,寫成:

重復記錄的處理

在我們進行關聯操作時,關聯條件字段的異常重復數據是影響運行效率和數據質量的一個重要因素。比如我們要根據enb_id字段關聯的兩張表,這兩張表中各有一千條數據的enb_id值為1(這是很常見的錯誤)。關聯后,重復字段的條目數就變為1000*1000。如果重復字段再多,數據量的指數級增長將極大影響運行效率。因此我們需要對數據進行去重處理。以下介紹一種最高效的刪除重復記錄的方法:

當然,要在熟悉業務邏輯的情況下進行去重,以免誤刪正常的重復數據。

WHERE、HAVING和ON的比較

WHERE和HAVING關鍵字都可以對查詢結果進行篩選,兩者的區別是WHERE的作用時間是在計算之前就完成的,而having是在計算后才起作用的。HAVING只會在檢索出所有記錄之后才對結果集進行過濾。 這個處理需要排序,總計等操作。顯然WHERE能在計算之前濾掉更多數據,效率更高。因此在不涉及計算的篩選中,應盡量使用WHERE。只有對計算之后的字段進行篩選時才考慮使用HAVING。

ON關鍵字實際上也是對數據進行篩選,只不過是在多表關聯時使用。需要注意的是,在我們常用的操作中,表關聯是最耗時的操作之一。尤其是兩張大表的關聯。因此我們應盡量在關聯之前對數據進行匯總和篩選,以減少關聯的數據量,提高運行效率。例如以下兩段代碼:

SQL-1:

SQL-2:

第一條SQL的邏輯是先將兩表進行關聯,再從關聯結果中篩選出符合條件的數據。而第二條SQL的邏輯是先從一張表中篩選出需要的數據,再進行關聯得到結果。顯然第二條SQL需要進行關聯的數據量更少,效率更高,尤其是WHERE條件可以濾掉大量數據的情況下。

靈活使用EXISTS關鍵字

在許多基於基礎表的查詢中,為了滿足一個條件,往往需要對另一個表進行連接.在這種情況下,通常我們會考慮使用IN關鍵字,如:

在這種情況下,使用EXISTS或NOT EXISTS通常是查詢效率更高的方法。在子查詢中,NOT IN子句將執行一個內部的排序和合並。無論在哪種情況下,NOT IN都是最低效的,因為它對子查詢中的表執行了一個全表遍歷。為了避免使用NOT IN ,我們可以把它改寫成外連接或NOT EXISTS。例如,上面的代碼我們可以改寫為:

同時,巧妙地使用EXIST還可以更高效地在查詢一對多表信息時實現DISTINCT效果。例如,我們要從工參中查詢KPI中出現的小區都屬於哪些地市。如果使用DISTINCT,實現方法如下:

但DISTINCT是一種很低效的查詢方式,應盡量避免使用。我們可以充分利用工參中enb_id唯一的特點使用EXIST更高效地實現同樣效果。代碼如下:

UNION ALL和UNION

UNION和UNION ALL都有合並數據的效果,但也有細微的區別。UNION ALL只是簡單的合並,將重復輸出兩個結果集合中相同記錄,而UNION會去除重復記錄。實際上,UNION就是先將兩個集合以UNION ALL的方式合並,然后在輸出最終結果前進行排序去重。因此UNION效率更低。當我們不需要對結果去重的時候,應盡量使用UNION ALL。

表關聯的優化

前面我們分析過,表關聯是最耗時間的常用操作之一。因此,表關聯優化的一個重要原則就是在關聯之前盡量減少兩張表格的數據量。看以下兩段SQL:

SQL-1:

SQL-2:

思考表連接的的SQL執行順序,前者兩張表JOIN后馬上篩選部分結果再與另一張表JOIN,后者先將三張表JOIN后再篩選。所以很明顯前者效率比后者高。還有另外一種寫法:

第三條和第一條SQL一樣效率不錯。從邏輯上看,似乎SQL會先將表JOIN后再篩選,但實際結果是先篩選再JOIN。因為SQL SERVER會內部分析,產生一個最優的執行計划,自動處理。而如果按照第二種寫法使用JOIN ON的話,就好像是使用強制命令,告訴數據庫,就是要按你的方式處理結果,數據庫只好服從。

但是有一點需要注意,上面的三種寫法使用的都是內關聯,三種方法的結果是一樣的,只是效率不同。如果使用外關聯,結果就有區別了。比如使用LEFT JOIN,第一種寫法中,將單表的過濾條件寫在ON后面,左表中不符合條件的數據也會被保留,無法起到過濾的作用。而第二種寫法先關聯再篩選,會濾掉不符合條件的數據。這點需要特別注意,以免發生邏輯上的錯誤。

后記

最后需要說明一點,如今數據庫的類型越來越多,數據庫的優化器也越來越智能。很多情況下,數據庫的優化器會根據數據情況自動對SQL進行優化。比如,Impala數據庫中可使用COMPUTE STATS語句收集表的統計信息,這樣在對表進行操作的時候,數據庫的優化器就可以自動進行SQL優化。再比如,針對表關聯中多表排列順序的問題,在基於規則的時代,查詢效率是和表的連接順序相關的,小表在左、大表在右的執行效率會高一些。但是現在基本上是基於代價的時代,所以大小表的順序和效率無關,數據庫優化器會自動去進行效率優化。但是,理解數據庫內部的代碼編譯邏輯是數據工程師的基本素質,即使很多時候優化器會幫助我們進行代碼優化,使我們減少很多思考,但是良好編程習慣的養成無論在任何時候都能使我們在工作中更加得心應手。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM