重中之重---語句執行順序前端
咱們先看看語句的執行順序面試
若是我沒記錯這是《SQL SERVER 2005技術內幕--查詢》這本書的開篇第一章第一節。書的做者也要讓讀者首先了解語句是怎麼樣的一個執行順序,由於不知道順序何談寫個好語句?sql
查詢的邏輯執行順序:數據庫
(1) FROM < left_table> 服務器
(3) < join_type> JOIN < right_table> (2) ON < join_condition> 網絡
(4) WHERE < where_condition> 架構
(5) GROUP BY < group_by_list> 函數
(6) WITH {cube | rollup}sqlserver
(7) HAVING < having_condition> 性能
(8) SELECT (9) DISTINCT (11) < top_specification> < select_list>
(10) ORDER BY < order_by_list>
標準的SQL 的解析順序爲:
(1).FROM 子句 組裝來自不一樣數據源的數據
(2).WHERE 子句 基於指定的條件對記錄進行篩選
(3).GROUP BY 子句 將數據劃分爲多個分組
(4).使用聚合函數進行計算
(5).使用HAVING子句篩選分組
(6).計算全部的表達式
(7).使用ORDER BY對結果集進行排序
執行順序:
1.FROM:對FROM子句中前兩個表執行笛卡爾積生成虛擬表vt1
2.ON:對vt1表應用ON篩選器只有知足< join_condition> 爲真的行才被插入vt2
3.OUTER(join):若是指定了 OUTER JOIN保留表(preserved table)中未找到的行將行做爲外部行添加到vt2 生成t3若是from包含兩個以上表則對上一個聯結生成的結果表和下一個表重複執行步驟和步驟直接結束
4.WHERE:對vt3應用 WHERE 篩選器只有使< where_condition> 爲true的行才被插入vt4
5.GROUP BY:按GROUP BY子句中的列列表對vt4中的行分組生成vt5
6.CUBE|ROLLUP:把超組(supergroups)插入vt6 生成vt6
7.HAVING:對vt6應用HAVING篩選器只有使< having_condition> 爲true的組才插入vt7
8.SELECT:處理select列表產生vt8
9.DISTINCT:將重複的行從vt8中去除產生vt9
10.ORDER BY:將vt9的行按order by子句中的列列表排序生成一個遊標vc10
11.TOP:從vc10的開始處選擇指定數量或比例的行生成vt11 並返回調用者
咱們瞭解了sqlserver執行順序,請之前不知道的看官們,反覆試驗反覆記憶!那麼咱們就接下來進一步養成平常sql好習慣,也就是在實現功能的同時又考慮性能的思想!
設計思路
設計思路說的有點大了,下面介紹幾個最多見的設計問題!
循環改批量
循環單條操做,請改爲批量操做,若是沒辦法修改,請儘可能想辦法修改!這算是最多見的吧:
上個小例子:
create table test_0607 (a int,b nvarchar(100)) declare @i int set @i = 1 while @i < 10000 begin insert into test_0607 select @i,'0607無顯示總體事務' set @i = @i + 1 end
drop table test_0607 create table test_0607 (a int,b nvarchar(100))
---加上事務 begin tran declare @i int set @i = 1 while @i < 10000 begin insert into test_0607 select @i,'0607 顯示總體事務' set @i = @i + 1 end ----結束事務,提交 commit
結果 : 8秒和0.8秒的區別,不用多說啥了吧! 凡事有利有弊,這種顯示開啓大事務要保證的總體的過程不會執行特別長的時間,若是執行的操做特別多並且時間長就是災難了!
下降語句複雜性
前文語句優化三板斧中已經介紹過,下降語句複雜性是常見的優化方式。這裏在說一下,致使語句特別複雜通常有兩個緣由:
對於第一種狀況,代碼看起來就很長很複雜,看起來很牛逼的代碼其實在高手看來都是很LOW的。而對於第二種,看起來代碼很簡潔,但通過SQL優化器的二次編譯,其實和第一種並沒有區別。這兩種的解決辦法都是下降複雜性,把一些能拆分出來的儘可能拆分出來放入臨時表或者表變量中,好比先把條件篩選性較強的幾張表關聯,而後把結果放入臨時表,在用臨時表和其餘表關聯。能夠理解成我有10張表關聯,我先拿5張表出來關聯,而後把結果放入臨時表,再跟另外5張表關聯。這樣這個查詢的複雜度由10張表的聯合變成 5+6,這樣下降了複雜語句複雜度。
複雜視圖也是如此,在視圖和外層關聯前,放入臨時表,再跟外層關聯。
子查詢也是如此,能夠分離出來成爲臨時表的子查詢,先分離出來。
對於表值函數,其實也是有內聯和表值之分:
---方式1:內聯 CREATE FUNCTION [dbo].[tvf_inline_Test]() RETURNS TABLE AS RETURN SELECT ProductID FROM Sales.SalesOrderHeader soh INNER JOIN Sales.SalesOrderDetail sod ON soh.SalesOrderID = sod.SalesOrderID ---此寫法能夠結合外層查詢二次編譯(也就是能夠利用外層的關聯條件及WHERE 條件)
---方式2:表值 CREATE FUNCTION [dbo].[tvf_multi_Test]() RETURNS @SaleDetail TABLE ( ProductId INT ) AS BEGIN INSERT INTO @SaleDetail SELECT ProductID FROM Sales.SalesOrderHeader soh INNER JOIN Sales.SalesOrderDetail sod ON soh.SalesOrderID = sod.SalesOrderID RETURN END ---此寫法不能應用外層條件篩選,若是數據量大會對性能產生影響。
高能預警:這裏說的是適當使用臨時表,我遇到的不少開發人員通常都有這樣一個過程。開始巨複雜的語句,知道使用臨時表之後,每一個步驟很小的操做都要用臨時表。這會給你的TempDB形成很大的壓力!
避免重複讀取
曾經遇到過不少這樣的程序,相似對商品有多種分析,而每種分析要作一些不一樣的處理,可是他們都會讀取同一份基礎數據商品和商品明細等。不少程序都是按照每種分析做爲一個單獨的存儲過程去處理,那麼也就是說有20種處理他們建立了20個存儲過程,而且每一個存儲過程的第一步,就是先讀取基礎數據--商品和明細等等。不巧的是商品和商品明細有巨大的數據量,雖然作了分表(按照月份,每一個表大概2QW數據),可是每一個存儲過程要讀取一年的數據,大概是2QW * 12 ,這麼龐大的數據巨量,查詢後被放入一張temp表,20個存儲過程順序執行,也就是說這份基礎數據天天晚上會被查詢20次! 基本上這個處理佔據了系統夜間維護的全部時間,有時甚至會跑不完影響白天正常業務!
也許你看完描述就會笑,誰會把處理設計成這個樣子?這不開玩笑麼?沒錯,解決這個問題其實超簡單,把20個存儲過程合成一個。讓基礎數據的查詢只查詢一次,放入臨時表,建立出下面邏輯處理須要的索引,在用這個臨時表分別作下面全部的處理。這樣一個夜間須要跑6小時以上的處理被縮短成40分鐘!(固然說的有點誇張,裏面還有些其餘的優化,√)
這裏就提到一個使用臨時表比較重要的問題,那就是相似上面的大量數據寫入臨時表,必定要用 先create 再 insert 的方式,不要直接使用 select into 臨時表的方式,不然就是災難了!
論索引的重要性
老生常談的話題了,我想全部公司招人的時候都會問到這樣的面試題: 什麼是索引,索引有哪些類,有何不一樣?等等....
索引是啥?什麼是彙集索引?什麼是非彙集索引?什麼是主鍵查找?什麼是主鍵掃描?什麼是索引查找?什麼是書籤查找?有啥區別? 這裏都不介紹,請自行百度!
不少開發人員意識不到索引到底對語句,甚至對系統有對重要。關於索引對系統的重要性請關注後續文章。
如何創建索引
最爲簡單粗暴的方式,當你寫完一條語句的時候,打開執行計劃,執行一下按照優化器的提示建立索引。
高能預警:這裏須要你的條件能夠用索引!好比 你的語句中 索引列不能帶函數,不能參與計算如 where productID/2 = @a ,不能有隱式轉換等!
創建索引後,一樣並非每一個查詢都會使用索引,在使用索引的狀況下,索引的使用效率也會有很大的差異。如上面缺失的索引咱們添加上之後再查詢!
索引查找(seek),通常爲最優(但查找也要看查找的篩選性),儘可能吧where 條件中的字段建成一個組合索引,而且包含要查詢select 中的字段。這裏就不繼續深刻了。
看懂執行計劃建立
如何看懂執行計劃這就是一個能夠寫幾百頁書的話題了,可是看懂執行計劃是作優化的重中之重了!之後的文章中會詳細講解。
經過執行計劃能夠看出語句的主要消耗到底在哪裏,另外配合set statistics io on 等分析讀次數,也是優化的關鍵,建立或優化索引頁是主要從這裏出發。
語句常規習慣
只返回須要的數據
返回數據到客戶端至少須要數據庫提取數據、網絡傳輸數據、客戶端接收數據以及客戶端處理數據等環節,若是返回不須要的數據,就會增長服務器、網絡和客戶端的無效勞動,其害處是顯而易見的,避免這類事件須要注意:
橫向來看:
縱向來看:
減小沒必要要的操做
寫語句以前,理清你的思路!
儘可能早的篩選
經常使用的寫法誤區(如下都是網上片面結論)
全部別人提到的方法到底有無效
-----------------------------------------------------------------------------------------------------
總結 :說到語句優化,有太多太多的注意,這些須要明白原理,能看懂執行計劃,而且不斷積累。
單單的幾篇優化大全是幫助是微乎其微的,另外要動手實踐,明白爲何這樣寫會好!