1. 程式人生 > >MSSQL Server查詢優化 (整理修改)

MSSQL Server查詢優化 (整理修改)

/***** 在SQLServer上檢視SQL語句的執行時間的方法 *******

比較簡單的查詢方法,通過查詢前的時間和查詢後的時間差來計算的

程式碼如下:
declare @begin_date datetime
declare @end_date datetime
select @begin_date = getdate()
<這裡寫上你的語句...>
select @end_date = getdate()
select datediff(ms,@begin_date,@end_date) as '用時/毫秒' 
OR:select (@[email protected]
_date) as execTime


2:方法二比較全面,將執行每個語句時採取的步驟作為行集返回,通過層次結構樹的形式展示出來
程式碼如下:
set statistics profile on
set statistics io on
set statistics time on
go
<這裡寫上你的語句...>
go
set statistics profile off
set statistics io off
set statistics time off

*************/

SQL執行順序

(1)、 FROM:對FROM子句中的前兩個表執行笛卡爾積(交叉聯接),生成虛擬表VT1。  

(2)、 ON:對VT1應用ON篩選器,只有那些使為真才被插入到TV2。
(3)、 OUTER (JOIN):如果指定了OUTER JOIN(相對於CROSS JOIN或INNER JOIN),保留表中未找到匹配的行將作為外部行新增到VT2,生成TV3。如果FROM子句包含兩個以上的表,則對上一個聯接生成的結果表和下一個表重複執行步驟1到步驟3,直到處理完所有的表位置。
(4)、 WHERE:對TV3應用WHERE篩選器,只有使為true的行才插入TV4。  

(5)、 GROUP BY:按GROUP BY子句中的列列表對TV4中的行進行分組,生成TV5。  

(6)、 CUTE|ROLLUP:把超組插入VT5,生成VT6。
(7)、 HAVING:對VT6應用HAVING篩選器,只有使為true的組插入到VT7。  

(8)、 SELECT:處理SELECT列表,產生VT8。
(9)、 DISTINCT:將重複的行從VT8中刪除,產品VT9。
(10)、ORDER BY:將VT9中的行按ORDER BY子句中的列列表順序,生成一個遊標(VC10)。
(11)、TOP:從VC10的開始處選擇指定數量或比例的行,生成表TV11,並返回給呼叫者

1.用select top xx 來限制使用者返回的行數或者SET ROWCOUNT來限制操作的行

2.儘量不用 "IS NULL", "<>", "!=", "!>", "!<", "NOT", "NOT EXISTS", "NOT IN", "NOT LIKE", and "LIKE '%500'",這些全表掃描,不會執行索引

  SELECT ID FROM T WHERE NUM IS NULL
  可以在NUM上設定預設值0,確保表中NUM列沒有NULL值( 應該沒什麼影響 ),然後這樣查詢:
  SELECT ID FROM T WHERE NUM=0

  ----------------------------------------------------------

  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” 

  -----------------------------------------------------------

  對UNION ALL結果集新增Where條件

 select * from (
 [SQL 語句 1] 
 UNION 
 [SQL 語句 2] 
 ) tmp
 where 姓名 like 'xxx%'

注意:union子句不能直接加order by,應用如下方法
select * From (select top 5  1 as order1,* from ProContent where ProductName like 'a%') as Tb1
union
select * From (select top 5  2 as order1,* from ProContent where ProductName like 'b%' order by order1, id) as Tb2
order by order1,id desc 
相同欄位,不必每個union 加order by xxx ,只需在任意一個union子句中新增一次即可,結果等價
如果子查詢的排序與總查詢相同,則在子查詢無需排序order by 

  -----------------------------------------------------------

  SELECT ID FROM T WHERE NUM IN(1,2,3)
  對於連續的數值,能用 BETWEEN 不要用 IN :
  SELECT ID FROM T WHERE NUM BETWEEN 1 AND 3”

3.不要在WHere字句中的列名加函式,如Convert,substring等,如果必須用函式的時候, 建立計算列再建立索引來替代.

   還可以變通寫法:WHERE SUBSTRING(firstname,1,1) = 'm'改為WHERE firstname like 'm%'(索引掃描),

   like 'a%' 使用索引

   like '%a' 不使用索引  

   不要在一句話裡再三的使 用相同的函式,浪費資源,將結果放在變數裡再呼叫更快

4.NOT IN會多次掃描表,使用EXISTS、NOT EXISTS ,IN , LEFT OUTER JOIN 來替代,特別是左連線,而Exists比IN更快,最慢的是NOT操作.

  在IN後面值的列表中,將出現最頻繁的值放在最前面,出現得最少的放在最後面,減少判斷的次數

5.MIN() 和 MAX()能使用到合適的索引,

 SELECT COUNT(*)的效率教低,儘量變通他的寫法,而EXISTS快

   不要對索引欄位進行運算,而要想辦法做變換,比如

   SELECT ID FROM T WHERE NUM/2=100
   應改為:
   SELECT ID FROM T WHERE NUM=100*2

6.如果要插入大的二進位制值到Image列,使用儲存過程,不要用內嵌INsert來插入(不知JAVA是否)。因為這樣應用程式首 先將二進位制值轉換成字串(尺寸是它的兩倍),伺服器受到字元後又將他轉換成二進位制值.儲存過程就沒有這些動作:
方法:Create procedure p_insert as insert into table(Fimage) values (@image),
在前臺調 用這個儲存過程傳入二進位制引數,這樣處理速度明顯改善。

7.Between在某些時候比IN速度更快,Between能夠更快地根據索引找到範圍。用查詢優化器可見到差別。
select * from chineseresume where title in ('男','女')
Select * from chineseresume where between '男' and '女'
是一樣的。由於in會在比較多次,所以有時會慢些

8.用OR的字句可以分解成多個sql查詢,並且 通過UNION 連線多個查詢

9.一般在GROUP BY 個HAVING字句之前就能剔除多餘的行,所以儘量不要用它們來做剔除行的工作。他們的執行順序應該如下最優:select 的Where字句選擇所有合適的行,Group By用來分組個統計行,Having字句用來剔除多餘的分組。這樣Group By 個Having的開銷小,查詢快.對於大的資料行進行分組和Having十分消耗資源。如果Group BY的目的不包括計算,只是分組,那麼用Distinct更快

10.一次更新多條記錄比分多次更新每次一條快,

11.(1) IF 沒有輸入負責人程式碼 THEN
code1=0
code2=9999
ELSE
code1=code2= 負責人程式碼
END IF
執行SQL語句為:
SELECT 負責人名 FROM P2000 WHERE 負責人程式碼>=:code1 AND負責人程式碼 <=:code2 


(2) IF 沒有輸入負責人程式碼 THEN
  SELECT 負責人名 FROM P2000
ELSE
code= 負責人程式碼
SELECT 負責人程式碼 FROM P2000 WHERE 負責人程式碼=:code
END IF

第一種方法只用了一條SQL語句,第二種方法用了兩條SQL語句。在沒有輸入負責人代 碼時,第二種方法顯然比第一種方法執行效率高,因為它沒有限制條件;在輸入了負責人程式碼時,第二種方法仍然比第一種方法效率高,不僅是少了一個限制條件, 還因相等運算是最快的查詢運算

12.資料型別儘量小,這裡的儘量小是指在滿足可以預見的未來需求的前提下的。

     儘量不要允許NULL,除非必要,可以用NOT NULL+DEFAULT代替。

    SELECT INTO後的WHERE子句,因為SELECT INTO把資料插入到臨時表,這個過程會鎖定一些系統表,如果這個WHERE子句返回的資料過多或者速度太慢,會造成系統表長期鎖定,諸塞其他程序。

13.日期查詢的例子:

WHERE DATEDIFF(DAY, 日期,'2005-11-30')=0應改為:WHERE 日期 >='2005-11-30' AND 日期 <'2005-12-1‘

WHERE DATEDIFF(DAY, 日期,'2005-11-30')>0應改為:WHERE 日期 <'2005-11-30‘

WHERE DATEDIFF(DAY, 日期,'2005-11-30')>=0應改為:WHERE 日期 <'2005-12-01‘

WHERE DATEDIFF(DAY, 日期,'2005-11-30')<0應改為:WHERE 日期>='2005-12-01‘

WHERE DATEDIFF(DAY, 日期,'2005-11-30')<=0應改為:WHERE 日期>='2005-11-30‘

14.sql not in與left join百萬級資料測試比較
select OrgId as 公司編碼,OrgName as 公司名稱
from Organise
where OrgLev=2
and item_id not in
(select OrgidS from WagesPerMonthHis
where WagesYear='2010' and WagesMonth=
'01' Group by OrgidS,OrgNameS)
order by Orgid


語句執行要33秒之久,記憶體和CPU在執行時都沒有出現瓶頸,以為是
程式碼如下 複製程式碼
(select OrgidS from WagesPerMonthHis
where WagesYear='2010' and WagesMonth=
'01' Group by OrgidS,OrgNameS)
這條語句執行緩慢所致,單獨執行這條卻發現執行速度很快,大約不到2秒就出來了,於是癥結出來了,是not in 這個全掃描關鍵詞帶來的效能下降.最直接的是導致頁面失去響應,一個關鍵功能使用不了.

試了not exist語句,發現效果是一樣的,並不象網上所說可以提高很多效能.

於是重新優化語句如下
程式碼如下 複製程式碼
select a.OrgId as 公司編碼,a.OrgName as 公司名稱,a.item_id
from Organise a
left outer join (select distinct b.OrgIdS from WagesPerMonthHis b
where WagesYear='2010' and WagesMonth='01') as b
on a.item_id = b.OrgidS
where a.OrgLev = 2
and b.OrgIdS is Null
order by 公司編碼

改用左外連線(其實左連線也可以)後,整個語句執行速度為400ms

14.大資料量排序

    在資料庫中有一張表mytable,資料記錄7000萬條,有如下兩條SQL語句

(1)select top 100 * from mytable order by operateDate

 (2)select * from (select top 100 * from mytable ) a   order  by a.operateDate  (快)


15.索引的建立規則

(1)、表的主鍵、外來鍵必須有索引;
(2)、資料量超過300的表應該有索引;
(3)、經常與其他表進行連線的表,在連線欄位上應該建立索引;
(4)、經常出現在Where子句中的欄位,特別是大表的欄位,應該建立索引;
(5)、索引應該建在選擇性高的欄位上;
(6)、索引應該建在小欄位上,對於大的文字欄位甚至超長欄位,不要建索引;
(7)、複合索引的建立需要進行仔細分析;儘量考慮用單欄位索引代替:
  (A)、正確選擇複合索引中的主列欄位,一般是選擇性較好的欄位;
  (B)、複合索引的幾個欄位是否經常同時以AND方式出現在Where子句中?單欄位查詢是否極少甚至沒有?如果是,則可以建立複合索引;否則考慮單欄位索引;
  (C)、如果複合索引中包含的欄位經常單獨出現在Where子句中,則分解為多個單欄位索引;
  (D)、如果複合索引所包含的欄位超過3個,那麼仔細考慮其必要性,考慮減少複合的欄位;
  (E)、如果既有單欄位索引,又有這幾個欄位上的複合索引,一般可以刪除複合索引;
(8)、頻繁進行資料操作的表,不要建立太多的索引;
(9)、刪除無用的索引,避免對執行計劃造成負面影響;