1.儘可能避免在WHERE子句中對字段進行NULL值判斷sql
在WHERE子句中對字段進行NULL值判斷會致使引擎放棄使用索引而進行全表掃描。如:數據庫
SELECT id FROM t WHERE num IS NULL
能夠在num字段設置默認值0,確保表中 num字段沒有NULL值,而後這樣查詢:編程
SELECT id FROM t WHERE num=0
2.儘可能避免在WHERE子句中使用!=或<>操做符服務器
在WHERE子句中使用!=或<>操做符將致使引擎放棄使用索引而進行全表掃描。優化器將沒法經過索引來肯定將要命中的行數,所以須要搜索該表的全部行。併發
3.儘可能避免在WHERE子句中使用OR來鏈接條件函數
在WHERE子句中使用OR鏈接條件,將會致使引擎放棄使用索引而進行全表掃描,如:post
SELECT id FROM t WHERE num=10 OR num=20
能夠改爲:性能
SELECT id FROM t WHERE num=10 UNION ALL SELECT id FROM t WHERE num=20
4.慎用IN和NOT IN大數據
由於IN會使系統沒法使用索引,而只能直接搜索表中的數據。如:優化
SELECT id FROM t WHERE num in(1,2,3)
對於連續的數值,能用BETWEEN就不要用IN
SELECT id FROM t WHERE num BETWEEN 1 AND 3
5.儘可能避免在索引過的字符數據中,使用'%'開頭搜索。
此狀況下引擎沒法利用索引,如:
SELECT * FROM t WHERE name Like '%Jhon%'
建議使用全文檢索。
6.必要時強制查詢優化器使用某個索引
如在WHERE子句中使用參數,也會致使全表掃描。由於SQL只有在運行時纔會解析局部變量,但優化程序不能講訪問計劃的選擇推遲到運行時,它必須在編譯時進行選擇。然而,若是在編譯時簡歷訪問計劃,變量的值仍是未知的,於是沒法做爲索引選擇的輸入項。以下面語句會進行全表掃描:
SELECT id FROM t WHERE num = @num
能夠改成強制查詢使用索引:
SELECT id FROM t WITH(INDEX(索引名)) WHERE num=@num
7.儘可能避免在WHERE子句中對字段進行表達式操做
這將致使引擎放棄使用索引而進行全表掃描。如:
SELECT * FROM t WHERE num/2=100
應改成:
SELECT * FROM t WHERE num=100*2
SELECT* FROM t WHERE SUBSTRING(num,1,4)='5678'
應改成:
SELECT * FROM t WHERE num LIKE '5678%'
SELECT * FROM t WHERE DATEDIFF(yy,num,GETDATE())>21
應改成:
SELECT * FROM t WHERE num<DATEADD(yy,-21,GETDATE())
即任何對列的操做都將致使表掃描,包括數據庫函數、計算表達式等;查詢時要儘量將操做移至右邊。
8.儘可能避免在WHERE子句中對字段進行函數操做
會致使引擎放棄使用索引而進行全表掃描,如:
SELECT id FROM t WHERE SUBSTRING(name,1,3)='abc' SELECT id FROM t WHERE DATEDIFF(day,createdate,'2015-01-30')=0
應改成:
SELECT id FROM t WHERE name like 'abc%' SELECT id FROM t WHERE createdate>='2015-01-30' AND createdate<'2015-01-31'
9.不要在WHERE子句中的"="左邊進行函數、運算符或其餘表達式運算
該操做會致使系統可能沒法正確使用索引。
10.若是索引是複合索引,要用該索引的第一個字段做爲條件
在使用索引字段做爲條件時,若是該索引是符合索引,則必須使用該索引中的第一個字段做爲條件時才能保證系統使用該索引,不然該索引將不會被使用。且應儘量讓字段順序與索引順序一致。
11.不少時候EXISTS是個好的選擇
SELECT num FROM t1 WHERE num in(SELECT num FROM t2)
用下面的語句替換:
SELECT num FROM t1 WHERE EXISTS(SELECT 1 FROM t2 WHERE num=t1.num)
SELECT SUM(t1.c1) FROM t1 WHERE (SELECT COUNT(*) FROM t2 WHERE t2.c2=t1.c2>0)
用下面的語句替換:
SELECT SUM(t1.c1) FROM t1 WHERE EXISTS(SELECT * FROM t2 WHERE t2.c2=t1.c2)
二者產生相同的結果,可是後者的效率明顯高於前者。由於後者不會產生大量鎖定的表掃描或索引掃描。
若是想校驗表裏是否存在某條記錄,不要用COUNT(*),那樣效率很低且浪費服務器資源。
能夠用EXISTS代替,如:
IF (SELECT COUNT(*) FROM t WHERE name='xxx')
能夠改爲:
IF EXISTS(SELECT * FROM t WHERE name='xxx')
常常須要寫一個T-SQL語句比較一個父結果集和子結果集,從而找到是否存在在父結果集中有而在子結果集中沒有的記錄,如:
SELECT t1.a FROM t1 WHERE NOT EXISTS(SELECT * FROM tb WHERE t1.a=t2.a) SELECT t1.a FROM t1 LEFT JOIN tb ON t1.a=t2.b WHERE t2.a IS NULL SELECT a FROM t1 WHERE a NOT IN(SELECT a FROM t2)
三種寫法均可以獲得相同的結果,可是效率一次下降。
12.儘可能使用表變量代替臨時表。
若是表變量包含大量數據,請注意索引很是有限(只有主鍵索引)
13.避免頻繁建立和刪除臨時表,以減小系統表資源消耗
14.合理利用臨時表
適當使用臨時表能夠提升效率,例如,當須要重複引用大型表或經常使用表中某個數據集時,可是,對於一次性事件,最好使用導出表。
15.臨時表的SELECT INTO和CREATE TABLE
在新建臨時表時,若是一次性插入數據量很大,可使用SELECT INTO代替CREATE TABLE,避免形成大量log,以提升速度;若是數據量不大,爲了緩和系統表資源,應先CREATE TABLE再INSERT。
16.若是使用到臨時表,在存儲過程的最後務必將全部的臨時表顯式刪除
先TRUNCATE TABLE,而後DROP TABLE,這樣能夠避免系統表長時間鎖定。
17.SET NOCOUNT ON 和SET NOCOUNT OFF
在全部的存儲過程和觸發器的開始處設置SET NOCOUNT ON,在結束時設置SET NOCOUNT OFF。無需再執行存儲過程和觸發器的每一個語句後向客戶端發送DONE_IN_PROC消息。
18.儘可能避免大事務操做,提升系統併發能力。
19.儘可能避免向客戶端返回大數據量
若數據量過大,應該考慮相應需求是否合理。
20.避免使用不兼容的數據類型
例如FLOAT和INT,CHAR和VARCHAR,BINARY和VARBINARY是不兼容的。數據類型的不兼容可能使優化器沒法執行一些原本能夠進行的優化操做。例如:
SELECT name FROM employee WHERE salary > 6000
在這條語句中,如salary字段是MONEY型的,則優化器很難對其進行優化,由於6000是整形。咱們應當在編程時將整形轉換爲MONEY類型,而不要等到運行時轉化。
21.充分利用鏈接條件
在某些狀況下,兩個表之間可能不止一個鏈接條件,這時在WHERE子句中將鏈接條件完整地寫上,有可能大大提升查詢速度。如:
SELECT SUM(account.amount) FROM account,card WHERE account.card_no=card.card_no SELECT SUM(account.amount) FROM account,card WHERE account.card_no=card.card_no AND account.account_no=card.account_no
第二句將比第一句執行快得多
22.使用視圖加速查詢
把表的一個子集進行排序並建立視圖,有時能加速查詢。它有助於比偶棉多重排序操做,並且在其餘方面黑能簡化優化器的工做。如:
SELECT cust.name,rcvbles.balance,...other columns FROM cust,rcvbles WHERE cust.customer_id=rcvbles.customer_id AND rcvbles.balance>0 AND cust.postcode>'98000' ORDER BY cust.name
若是這個查詢要被執行屢次而不止一次,能夠把全部未付款的客戶找出來放在一個視圖中,並按客戶的名字進行排序:
CREATE VIEW v_cust_rcvbles AS SELECT cust.name,rcvbles.balance,...other columns FROM cust,rcvbles WHERE cust.customer_id=rcvbles.customer_id AND rcvbles.balance>0 ORDER BY cust.name
而後如下面的方式在視圖中查詢:
SELECT * FROM v_cust_rcvbles WHERE postcode>'98000'
視圖中的行要比主表中的行少,並且物理順序就是所要求的順序,減小了磁盤I/O,因此查詢工做量能夠獲得大幅減小。
23.能用DISTINCT的就不用GROUP BY
SELECT id FROM products WHERE price>10 GROUP BY id
可改成:
SELECT DISTINCT id FROM products WHERE price>10
24.能用UNION ALL就不要用UNION
UNION ALL不執行SELECT DISTINCT函數,這樣就會減小不少沒必要要的資源
25.儘可能不要用SELECT INTO語句
SELECT INTO語句會致使表鎖定,阻止其餘用戶訪問該表。
26.並非全部索引對查詢都有效
SQL是根據表中數據來進行查詢優化的,當索引列有大量數據重複時,SQL查詢可能不會去利用索引,如表中有字段sex,male,female幾乎各一半,那麼即便在sex上創建了索引也對查詢效率起不了做用。
27.索引並非越多越好
索引當然能夠提升相應的SELECT效率,但同時也下降了INSERT和UPDATE的效率,由於INSERT或UPDATE時有可能會重建索引,因此怎樣建索引須要慎重考慮。一個表的索引數量最好不要超過6個。
28.儘量避免更新CLUSTERED索引數據列
由於CLUSTERED索引數據列的順序就是表記錄的物理存儲順序,一旦該列值改變將致使整個表記錄的順序的調整,會耗費至關大的資源。若應用系統須要頻繁更新CLUSTERED索引數據列,那麼須要考慮是否應該將該索引建爲CLUSTERED索引。
29.儘可能使用數字型字段
若只含數值信息的字段儘可能不要設計爲字符型,這會下降查詢和鏈接的性能,並會增長存儲開銷。這是由於引擎在處理查詢和鏈接時會逐個比較字符串中每一個字符,而對於數字型而言只須要比較一次。
30.儘量使用VARCHAR/NVARCHAR替代CHAR/NCHAR
由於首先變長字段存儲空間小,能夠節省存儲空間,其次對於查詢來講,在一個相對較小的字段內搜索效率顯然要高。
31.不要使用SELECT * FROM
用具體字段列表代替"*",不要返回任何用不到的字段
32.儘可能避免使用遊標
遊標效率較差,若是遊標操做的數據超過1萬行就應該考慮改寫
33.嘗試使用基於集的方法
在使用基於遊標的方法或臨時表以前,先尋找基於集的解決方案