SQL SERVER調優常用方法


說起SQL SERVER的調優,我想大伙也很想知道這方面的知識。本人也正在探索的路上,大家有什么好的意見,歡迎一起探討、研究。博取眾人之長,才能揚長避短。
本文中的內容主要是摘自《程序員的SQL金典》,如若大家想拜讀,可在網上下載拜讀(當然最好的方式還是購買作者的書)。
關於調優的方案,有涉及硬件方面的知識,也有涉及軟件方面的知識。但本人只是個軟件方面的IT男,所以只是記錄軟件方面的內容。
其實關於SQL SERVER或者是其它數據庫來講,有些優化手段都是一致的。比如常規的方式有如下幾種方式:

 

創建必要的索引

 

大學讀書時就聽說過數據庫里面的索引,一直沒去深究過,也在無知無畏中進行了四年多的開發生涯,想來慚愧的很,今天有幸了解,頓感人生之闊然開朗一般。索引,不單是數據庫里面才有,像我們寫代碼不也碰到數組也有索引嘛。索引就是已經按照某一種固定好的方式排序好內容,然后我們再去通過索引位置來定位到它。說到SQL SERVER的索引,有必要講講兩個概念。分別是聚簇索引和非聚簇索引。
1、聚簇索引:就是索引存儲的方式跟內容物理存儲的方式一致
2、非聚簇索引:就是索引存儲的方式跟內容物理存儲的順序不一致
下面簡單說明兩者的區別。最簡單的例子就是漢語詞典的方式,對於一本漢語詞典來說,它的物理存儲順序是已經固定好了的,是通過拼音的順序排列好的,這也就是說,漢語詞典的物理存儲方式就是通過拼音的方式來存儲。比如以拼音來建立的索引,就是聚簇索引。WHY?因為索引的存儲跟漢語詞典的物理存儲是一致的,也就是上面概念所說的分類。那么非聚簇索引是什么情況,比如像漢語詞典里面偏旁部首是一種索引的話,那么偏旁部首的索引就會是非聚簇索引了。WHY?因為它跟漢語詞典原有的物理存儲方式不一致。當創建聚簇索引時需要每一張表只能有一個聚簇索引,因為表中數據的物理順序只能有一個,而非聚集索引則可以創建多個

注:由於索引需要占據一定的存儲空間,而且索引也會降低數據插入、更新和刪除的速度,所以應該只創建必要的索引,一般是在檢索的字段創建索引。 對於刪除來說,索引會造成碎片的問題。因為當我們刪除一條記錄的時候,對應的索引並不會刪除。造成創建的索引被閑置,一旦閑置的索引碎片多了,就會影響查詢的效率。系統的垃圾碎片也是一樣的道理,需要定期清除。對於索引來說,發現使用索引搜索的速度慢了,就需要定期去重建索引,重建索引將會先刪除之前創建的索引,然后再新建新的索引,主流數據庫管理系統都提供了重建索引的功能。

 

使用預編譯查詢

 

很多人會使用存儲過程把SQL語句預先編譯起來,以此來達到優化的目的。有的項目是根據用戶的輸入來動態執行SQL語句,不管何種方式,都使用參數化的方式來執行,這樣不僅可以避免SQL注入漏洞攻擊,最重要數據庫會對這些參數化SQL執行預編譯,這樣第一次執行的時候DBMS會為這個SQL語句進行查詢優化並且執行預編譯,這樣以后再執行這個SQL 的時候就直接使用預編譯的結果,這樣可以大大提高執行的速度。

 

調整WHERE 子句中的連接順序(這個不是很懂?)

 

DBMS 一般采用自下而上的順序解析WHERE 子句,根據這個原理,表連接最好寫在其他WHERE條件之前,那些可以過濾掉最大數量記錄。
比如下面的SQL語句性能較差:

SELECT [FName],[FCity]
,[FAge]
,[FSalary]
FROM T_Person WHERE FSalary > 50000 AND FPosition= ‘MANAGER’ AND 25 < (SELECT COUNT(FName) FROM T_Manager WHERE FManagerId=2);

我們將子查詢的條件放到最前面,下面的SQL語句性能比較好:

SELECT [FName],[FCity]
,[FAge]
,[FSalary]
FROM T_Person WHERE 25 < (SELECT COUNT(FName) FROM T_Manager WHERE FManagerId=2) AND FSalary > 50000 AND FPosition= ‘MANAGER’ ;

 

SELECT語句中避免使用'*'

 

對於SELECT * FROM TABLE這種方式,我想很多人都會這么去查詢。WHY?一方面是因為系統的數據量級別還比較低,二來也圖方便。但是隨着項目的數據量瘋長,系統的性能急速下降之后,優化的每一種方式都需要引起我們的重視。像查詢這種方式來講,如果不用檢索出所有列的情況,盡量指定查詢的列。這能有效減輕網絡的負載和服務器資源的消耗。即使確實需要檢索所有列,也不要使用SELECT *,因為這是一個非常低效的方法,DBMS在解析的過程中,會將*依次轉換成所有的列名,這意味着將耗費更多的時間。在SQL Server Management Studio工具里面查詢圖省事,數據量不大我覺得還是可以的:),但至少在代碼里面是不建議這么處理。

 

多條SQL語句壓縮到一句SQL中去執行

 

對於SQL語句的執行,每次都要建立網絡連接、進行權限校驗、進行SQL語句的查詢優化、發送執行結果,這個過程是非常耗時的,因此應該盡量避免過多的執行SQL語句,能夠壓縮到一句SQL執行的語句就不要用多條來執行。

 

使用表的別名

 

當在 SQL 語句中連接多個表時,請使用表的別名並把別名前綴於每個列名上。這樣就可以減少解析的時間並減少那些由列名歧義引起的語法錯誤。

 

用EXISTS替代IN   (這個是否就沒有使用IN的必要了?)

 

在查詢中,為了滿足一個條件,往往需要對另一個表進行聯接,在這種情況下,使用EXISTS而不是IN 通常將提高查詢的效率,因為IN 子句將執行一個子查詢內部的排序和合並。下面的語句2 就比語句1 效率更加高。

語句 1:

SELECT  [FName],[FSalary],[FNumber] FROM T_Employee
WHERE FNumber> 0
AND FDEPTNO IN (SELECT FNumber
FROM T_Department
WHERE FMangerName = 'Tome')

語句 2:

SELECT [FName],[FSalary],[FNumber] FROM T_Employee
WHERE FNumber > 0
AND EXISTS (SELECT 1
FROM T_Department
WHERE T_Department. FDEPTNO = T_Employee.FNumber
AND FMangerName = ‘MELB’)

 

用表連接替換EXISTS  (這個不是很懂,不知道執行方式是怎么樣的?感覺都一樣)

 

通常來說,表連接的方式比EXISTS 更有效率,因此如果可能的話盡量使用表連接替換EXISTS。下面的語句2 就比語句1 效率更加高。
語句 1:

SELECT FName FROM T_Employee
WHERE EXISTS
(
SELECT 1 FROM T_Department
WHERE T_Employee.FDepartNo= FNumber
AND FKind='A'
);

語句 2:

SELECT FName FROM T_Department, T_Employee
WHERE T_Employee. FDepartNo = T_Departmen. FNumber
AND FKind = ‘A’ ;

 

避免在索引列上使用計算

 

在 WHERE 子句中,如果索引列是計算或者函數的一部分,DBMS 的優化器將不會使用索引而使用全表掃描。
例如下面的SQL語句用於檢索月薪的12倍大於兩萬五千元的員工:

SELECT [TName],[FSalary] FROM T_Employee
WHERE FSalary * 12 >25000;

由於在大於號左邊的是FSalary與12 的成績表達式,這樣DBMS 的優化器將不會使用字段FSalary的索引,因為DBMS必須對T_Employee表進行全表掃描,從而計算FSalary * 12 的值,然后與25000 進行比較。將上面的SQL語句修改為下面的等價寫法后DBMS將會使用索引查找,從而大大提高了效率:

SELECT [FName],[FSalary],[FNumber] FROM T_Employee
WHERE FSalary >25000/12;

同樣的,不能在索引列上使用函數,因為函數也是一種計算,會造成全表掃描。下面的語句2就比語句1 效率更加高。
語句 1:

SELECT [FAmount],[FName] FROM T_Example
WHERE ABS(FAmount)=300

語句2:

SELECT [FAmount],[FName] FROM T_Example
WHERE FAmount=300 OR FAmount=-300

 

用UNION ALL 替換UNION

 

當 SQL 語句需要UNION 兩個查詢結果集合時,即使檢索結果中不會有重復的記錄,如果使用UNION這兩個結果集同樣會嘗試進行合並,然后在輸出最終結果前進行排序。因此,如果檢索結果中不會有重復的記錄的話,應該用UNION ALL替代UNION,這樣效率就會因此得到提高。下面的語句2 就比語句1效率更加高。
語句 1:

SELECTACCT_NUM, BALANCE_AMT
FROM DEBIT_TRANSACTIONS1
WHERE TRAN_DATE = '20010101'
UNION
SELECTACCT_NUM, BALANCE_AMT
FROM DEBIT_TRANSACTIONS2
WHERE TRAN_DATE ='20010102'

語句2:

SELECTACCT_NUM, BALANCE_AMT
FROM DEBIT_TRANSACTIONS1
WHERE TRAN_DATE ='20010101'
UNION ALL
SELECTACCT_NUM, BALANCE_AMT
FROM DEBIT_TRANSACTIONS2
WHERE TRAN_DATE = '20010102'

 

避免隱式類型轉換造成的全表掃描

 

T_Person 表的字符串類型字段FLevel 為人員的級別,在FAge 字段上建有索引。我們執行下面的SQL語句用於檢索所有級別等於10的員工:

SELECT FId,FAge,FName
FROM T_Person
WHERE FAge=10

在這個SQL 語句中,將字符串類型字段FLevel 與數值10 進行比較,由於在大部分數據庫中隱式轉換類型中數值類型的優先級高於字符串類型,因此DBMS會對FAge字段進行隱式類型轉換,相當於執行了下面的SQL語句:

SELECT FId,FAge,FName
FROM T_Person
WHERE TO_INT(FAge)=10

由於在索引字段上進行了計算,所以造成了索引失效而使用全表掃描。因此應將SQL語句做如下修改:

SELECT FId,FAge,FName
FROM T_Person
WHERE FAge='10'

 

防止檢索范圍過寬

 

如果DBMS 優化器認為檢索范圍過寬,那么它將放棄索引查找而使用全表掃描。下面是幾種可能造成檢索范圍過寬的情況:
使用IS NOT NULL或者不等於判斷,可能造成優化器假設匹配的記錄數太多。
使用LIKE 運算符的時候,"a%"將會使用索引,而"a%c"和"%c"則會使用全表掃描,因為"a%c"和"%c"不能被有效的評估匹配的數量。


免責聲明!

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



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