SQL Server數據庫性能優化(一)之 優化SQL 語句

最近工做上基本沒什麼需求(好吧 不是最近是很久了,因此隨便看看基礎的東西來填補本身的空白)html

原文出自:http://www.blogjava.net/allen-zhe/archive/2010/07/23/326927.html   轉載請保留java

數據庫優化主要能夠從如下幾個方面入手程序員

(1)架構級別,表結構設計:如良好的系統和數據庫設計sql

(2)代碼語句級別:優質的SQL編寫數據庫

(3)索引設計:合適的數據表索引設計服務器

(4)硬件因素:網絡性能、服務器的性能、操做系統的性能,甚至網卡、交換機等網絡

 

這裏主要討論最容易修改優化的 SQL 語句數據結構

準則1:1. 按需索取字段,跟「SELECT *」說拜拜架構

 字段的提取必定要按照「用多少提多少」的原則,避免使用「SELECT *」這樣的操做。數據庫設計

作了這樣一個實驗,表tblA有1000萬數據:

select top 10000 c1, c2, c3, c4 from tblA order by c1 desc  --用時:4673毫秒
select top 10000 c1, c2, c3 from tblA order by c1 desc --用時:1376毫秒
select top 10000 c1, c2 from tblA order by c1 desc --用時:80毫秒

由此看來,咱們每少提取一個字段,數據的提取速度就會有相應的提高。但提高的速度還要看您捨棄的字段的大小來判斷。
另外,關於「SELECT *「的問題,能夠參考這篇文章:
http://www.cnblogs.com:80/goodspeed/archive/2007/07/20/index_coverage.html  //此文章評論爭議很大 因此此處不推薦閱讀

 

準則2:2. 字段名和表名要寫規範,注意大小寫
這一點要多注意,若是大小寫寫錯的話,雖然SQL仍然能正常執行,但數據庫系統會花必定的開銷和時間先要把您寫的規範成正確的,而後再執行SQL。寫對的話,這個時間就省了。
正常的:    select top 10 dteTransaction, txtSystem_id from tblTransactionSystem
不當心的:select top 10 dtetransaction, txtsystem_id from tbltransactionsystem

 

準則3:3. 適當使用過渡表
把表的一個子集進行排序並建立臨時表,有時能加速查詢。它有助於避免多重排序操做,並且在其餘方面還能簡化優化器的工做。例如:

 

SELECT cust.name,rcvbles.balance,……other   columns     
FROM cust,rcvbles     
WHERE cust.customer_id = rcvlbes.customer_id     
AND rcvblls.balance>0     
AND cust.postcode>98000」     
ORDER BY cust.name

  若是這個查詢要被執行屢次而不止一次,能夠把全部未付款的客戶找出來放在一個臨時文件中,並按客戶的名字進行排序:    

 

SELECT cust.name,rcvbles.balance,……other   columns     
INTO temp_cust_with_balance     
FROM cust,rcvbles     
WHERE cust.customer_id = rcvlbes.customer_id     
AND rcvblls.balance>0     
ORDER BY cust.name

 

  而後如下面的方式在臨時表中查詢:     

 

SELECT cl,c2 FROM temp_cust_with_balance WHERE  postcode>98000

 

臨時表中的行要比主表中的行少,並且物理順序就是所要求的順序,減小了磁盤I/O,因此查詢工做量能夠獲得大幅減小。注意:過渡臨時表建立後不會反映主表的修改。在主表中數據頻繁修改的狀況下,注意不要丟失數據。

準則4. 別在where條件中作函數計算
這樣作的後果是將在每一個行上進行運算,這將致使該列的索引失效而觸發全表掃描。以下SQL:

select * from users where YEAR(dteCreated) < 2007

能夠改爲

select * from users where dteCreated <2007-01-01

這樣會使用針對dteCreated的索引,提升查詢效率。

準則5. IN(NOT IN)操做符與EXISTS(NOT EXISTS)操做符
有時候會將一列和一系列值相比較。最簡單的辦法就是在where子句中使用子查詢。在where子句中可使用兩種方式的子查詢。以下:
第一種方式使用IN操做符:

select a.id from tblA a where a.id in (select b.id from tblB b)

 

第二種方式使用EXIST操做符:

select a.id from tblA a where exists (select 1 from tblB b where b.id = a.id)

 

用IN寫出來的SQL的優勢是比較容易寫及清晰易懂,這比較適合現代軟件開發的風格。可是用IN的SQL性能老是比較低的,而第二種格式要遠比第一種格式的效率高。從SQL執行的步驟來分析用IN的SQL與不用IN的SQL有如下區別:
SQL試圖將其轉換成多個表的鏈接,若是轉換不成功則先執行IN裏面的子查詢,再查詢外層的表記錄,若是轉換成功則直接採用多個表的鏈接方式查詢。因而可知用IN的SQL至少多了一個轉換的過程。通常的SQL均可以轉換成功,但對於含有分組統計等方面的SQL就不能轉換了。
第二種格式中,子查詢以’select 1’開始。運用EXISTS子句無論子查詢從表中抽取什麼數據它只查看where子句。這樣優化器就沒必要遍歷整個表而僅根據索引就可完成工做(這裏假定在where語句中使用的列存在索引)。相對於IN子句來講,EXISTS使用相連子查詢,構造起來要比IN子查詢困難一些。
經過使用EXIST,數據庫系統會首先檢查主查詢,而後運行子查詢直到它找到第一個匹配項,這就節省了時間。數據庫系統在執行IN子查詢時,首先執行子查詢,並將得到的結果列表存放在一個加了索引的臨時表中。在執行子查詢以前,系統先將主查詢掛起,待子查詢執行完畢,存放在臨時表中之後再執行主查詢。這也就是使用EXISTS比使用IN一般查詢速度快的緣由。
同時應儘量使用NOT EXISTS來代替NOT IN,儘管兩者都使用了NOT(不能使用索引而下降速度),NOT EXISTS要比NOT IN查詢效率更高。

準則6. IS NULL 或 IS NOT NULL操做(判斷字段是否爲空)
不能用null做索引,任何包含null值的列都將不會被包含在索引中,由於B樹索引是不索引空值的。即便索引有多列這樣的狀況下,只要這些列中有一列含有null,該列就會從索引中排除。也就是說若是某列存在空值,即便對該列建索引也不會提升性能。
任何在where子句中使用is null或is not null的語句優化器是不容許使用索引的。
推薦方案:用其它相同功能的操做運算代替,如a is not null 改成 a>0 或a>’等。另外還設置字段不容許爲空,而用一個缺省值代替空值,如一個datetime字段,能夠將默認時間設爲「1900-01-01」。

準則7. > 及 < 操做符(大於或小於操做符)
       大於或小於操做符通常狀況下是不用調整的,由於它有索引就會採用索引查找,但有的狀況下能夠對它進行優化,如一個表有100萬記錄,一個數值型字段A,30 萬記錄的A=0,30萬記錄的A=1,39萬記錄的A=2,1萬記錄的A=3。那麼執行A>2與A>=3的效果就有很大的區別了,由於 A>2時sql會先找出爲2的記錄索引再進行比較,而A>=3時sql則直接找到=3的記錄索引。可結合非彙集索引一塊兒考慮。

準則8. LIKE操做符
LIKE 操做符能夠應用通配符查詢,裏面的通配符組合可能達到幾乎是任意的查詢,可是若是用得很差則會產生性能上的問題,如LIKE ‘%5400%’ 這種查詢不會引用索引,而LIKE ‘X5400%’則會引用範圍索引。由於索引的擺放是依據字段值升序或降序排列,like'%*'這種用法,不能利用有序的數據結構,利用二分法查找數據。一個實際例子:用YW_YHJBQK表中營業編號後面的戶標識號可來查詢營業編號 YY_BH LIKE ‘%5400%’ 這個條件會產生全表掃描,若是改爲YY_BH LIKE ’X5400%’ OR YY_BH LIKE ’B5400%’ 則會利用YY_BH的索引進行兩個範圍的查詢,性能確定大大提升。

準則9. 查詢條件中的適當與不適當
查詢參數能夠包含一下操做:=、<、>、>=、<=、BETWEEN、部分like。其中,like當這樣使用時會用到索引:like '*%',但like'%*'就用不到索引。
不適當的查詢參數有:NOT 、!= 、<>、 !>、 !< 、NOT EXISTS、 NOT IN 、NOT LIKE等,還有一些不當的用法,例如:對數據進行計算,負向查詢、等號左邊使用函數、使用OR。上述語法都用不上索引,下降程序的效率。

準則10. 慎用DELETE

通常在存儲過程當中或多或少都會實現一些刪除數據的邏輯。對小數量的表來講,問題卻是不大。但對於大數據量的表來講,採用delete刪除數據會對儲存過程的性能產生必定的影響。由於delete採用的是全表逐條掃描的方式進行,是一種事務性操做,會計入SQL Server的事務日誌中。不但增長了運行時間,同時也頻繁寫入LOG文件,致使LOG文件過大,過度消耗磁盤空間。因此,能夠用truncate操做代替delete,truncate並不會計入事務日誌中,同時也是不帶條件的刪除,執行速度很快。又或者直接drop掉表從新建立,有時都會比delete來得快。

 

 

PS: 第10點引出的兩種清空SQL Server日誌文件的方法

一種方法:清空日誌。

1.打開查詢分析器,輸入命令DUMP TRANSACTION 數據庫名 WITH NO_LOG

2.再打開企業管理器--右鍵你要壓縮的數據庫--全部任務--收縮數據庫--收縮文件--選擇日誌文件--在收縮方式裏選擇收縮至XXM,這裏會給出一個容許收縮到的最小M數,直接輸入這個數,肯定就能夠了。

另外一種方法有必定的風險性,由於SQL SERVER的日誌文件不是即時寫入數據庫主文件的,如處理不當,會形成數據的損失。

1: 刪除LOG

分離數據庫 企業管理器->服務器->數據庫->右鍵->分離數據庫

2:刪除LOG文件

附加數據庫 企業管理器->服務器->數據庫->右鍵->附加數據庫

此法生成新的LOG,大小隻有500多K。

下邊的內容來自 <程序員SQL金典>

準則11:儘可能將多條SQL語句壓縮到一句SQL中

每次執行SQL的時候都要創建網絡鏈接,進行權限校驗,進行SQL語句的查詢優化/發送執行結果,這個過程是很是耗時的,所以儘可能避免過多的執行SQL語句

//這一條  本人以爲有異議  由於這樣會致使sql語句非原子性的存在  耦合性更高 若是業務發生變更的話 須要從新修改SQL語句這是很沒必要要的   因此結合的時候要注意

準則12:使用表的別名

當在SQL 語句中鏈接多個表時,最好使用表的別名,並把別名前綴置於每個列名上,這樣能夠減小解析的時間,也能夠減小因爲列名的歧義產生的錯誤,好比 兩張表中的 列名 很接近。

準則13:用錶鏈接代替Exists

一般來講錶鏈接的方式比Exists 更有效率,所以若是可能的話儘可能使用錶鏈接替換Exists。 

//這一條本人有異議,由於錶鏈接會過長的佔用某張表,若是一張表須要快速的操做,則在其餘地方出現鏈接使用這張表時,則會使總體的執行效率變慢,儘管鏈接的表可能不受影響。 這也是爲何不少大型系統不容許多張錶鏈接操做的

準則14:避免在索引列上使用計算

在WHERE 字句中,若是索引列是計算或者函數的部分,DBMS的優化器將不會使用索引而進行全表掃描。

準則15:避免隱式類型的轉換形成的全表掃描

在大部分數據庫的隱式轉換類型中數值類型的優先級高於字符串類型,所以DBMS會對比較時的不一樣類型作隱式轉換,有時會將字符串類型變爲數值類型致使索引失效而進行全表掃描 

準則16:防止檢索範圍過寬

若是DBMS優化器認爲檢索範圍過寬,那麼他將放棄索引查找而使用全表掃描,下面是形成檢索範圍過寬的狀況:

1使用 IS not Null 或者不等於判斷,可能形成優化器假設匹配的記錄數量太大。

2使用LIKE的時候 a%會使用索引而 a%b %c則會使用全表掃描,所以a%b %c不能有效的評估匹配的數量

準則17:必要時採用Union ALL 替換Union

二者區別是 Union ALL 會查找全部結果  而Union 會合並兩張表的重複記錄

假若兩張表的數據所有惟一時,Union 仍然會試圖在結果集中進行合併

相關文章
相關標籤/搜索