SQL Server 查詢性能優化——索引與SARG(一)


這一篇文章修修改改,已經寫了很久了,還是感覺好像自己沒講清楚,鑒於本人水平,就先這樣寫吧,待本人水平提高之后,再進行修補。

在寫作的過程也學習到了,SQL查詢優化程序也並不一定會使用查詢參數中字段的相關索引,而是根據查詢數據量的多少而產生的查詢成本,來決定是使用查詢參數中的字段索引,還是使用聚集索引或全表掃描。

中心思想就是關於SQL語句的“查詢參數”(SARG)與索引的使用。符合SARG格式的數據肯定會使用到相應的索引呢?先給出答案,不是。

例如:Select * from WBK_PDE_LIST_ORG where cop_g_no='11000' ,假設在cop_g_no上建立了非聚集索引,那么當查詢語句得出的結果數量小於某個數量閥值時,例如查詢結果的數量小於600條時,會使用到非聚集索引,但當查詢結果數量大於600條時,卻可能不會使用非聚集索引,可能會使用聚集索引或全表掃描。

 

在編寫SQL語句的WHERE 子句時,你是否考慮過WHERE子句中的條件參數的編寫格式要符合“ (查詢參數:SARG )”規則,SQL SERVER的查詢優化程序才能建立有效的利用索引的計划。

在進行具體分析之前,首先建立以下索引。當然索引2、3與索引4、5的名稱需要自己修改。

序號

索引類型

 SQL語句

1

主鍵聚集索引

ALTER TABLE [dbo].[WBK_PDE_LIST_ORG_HISTROY] ADD  CONSTRAINT [PK_WBK_PDE_LIST_ORG_HISTROY] 

PRIMARY KEY CLUSTERED(

[WBOOK_NO] ASC,

[G_NO] ASC

)WITH (PAD_INDEX  = OFF, STATISTICS_NORECOMPUTE  = OFF, SORT_IN_TEMPDB = OFF

, IGNORE_DUP_KEY = OFF, ONLINE = OFF, ALLOW_ROW_LOCKS  = ON

, ALLOW_PAGE_LOCKS  = ON) ON [PRIMARY]

2

非聚集索引(無INCLUDE)

 CREATE NONCLUSTERED INDEX [idx_WBK_PDE_LIST_QTY1] ON [dbo].[WBK_PDE_LIST_ORG_HISTROY] 

(

[QTY_1] ASC

) WITH (PAD_INDEX  = OFF, STATISTICS_NORECOMPUTE  = OFF, SORT_IN_TEMPDB = OFF

, IGNORE_DUP_KEY = OFF, DROP_EXISTING = OFF, ONLINE = OFF, ALLOW_ROW_LOCKS  = ON

, ALLOW_PAGE_LOCKS  = ON) ON [PRIMARY]

3

 

CREATE NONCLUSTERED INDEX [idx_WBK_PDE_LIST_COP_G_NO] ON [dbo].[WBK_PDE_LIST_ORG_HISTROY] 

(

[COP_G_NO] ASC

) WITH (PAD_INDEX  = OFF, STATISTICS_NORECOMPUTE  = OFF, SORT_IN_TEMPDB = OFF,

 IGNORE_DUP_KEY = OFF, DROP_EXISTING = OFF, ONLINE = OFF, ALLOW_ROW_LOCKS  = ON, 

ALLOW_PAGE_LOCKS  = ON) ON [PRIMARY]

4

非聚集索引(有INCLUDE)

 CREATE NONCLUSTERED INDEX [idx_WBK_PDE_LIST_QTY1] ON [dbo].[WBK_PDE_LIST_ORG_HISTROY] 

(

[QTY_1] ASC

)

INCLUDE ( [WBOOK_NO],[G_NO],[CODE_T],[COP_G_NO],[UNIT_1],[TRADE_TOTAL],[GROSS_WT])

 WITH (PAD_INDEX  = OFF, STATISTICS_NORECOMPUTE  = OFF, SORT_IN_TEMPDB = OFF

, IGNORE_DUP_KEY = OFF, DROP_EXISTING = OFF, ONLINE = OFF, ALLOW_ROW_LOCKS  = ON, 

ALLOW_PAGE_LOCKS  = ON) ON [PRIMARY]

5

 

CREATE NONCLUSTERED INDEX [idx_WBK_PDE_LIST_COP_G_NO] ON [dbo].[WBK_PDE_LIST_ORG_HISTROY] 

(

[COP_G_NO] ASC

)

INCLUDE ( [WBOOK_NO],[G_NO],[CODE_T],[QTY_1],[UNIT_1],[TRADE_TOTAL],[GROSS_WT])

 WITH (PAD_INDEX  = OFF, STATISTICS_NORECOMPUTE  = OFF, SORT_IN_TEMPDB = OFF,

 IGNORE_DUP_KEY = OFF, DROP_EXISTING = OFF, ONLINE = OFF, ALLOW_ROW_LOCKS  = ON, 

ALLOW_PAGE_LOCKS  = ON) ON [PRIMARY]

     

 

 

 

Index Seek 運算符利用索引的查找功能從非聚集索引中檢索行。

Index Scan 運算符從 Argument 列中指定的非聚集索引中檢索所有行。如果可選的 WHERE:() 謂詞出現在 Argument 列中,則只返回滿足該謂詞的那些行。

Clustered Index Scan 運算符會掃描查詢執行計划的 Argument 列中指定的聚集索引。如果出現可選 WHERE:()謂詞,則只返回滿足該謂詞的行。

Clustered Index Seek 運算符可以利用索引的查找功能從聚集索引中檢索行。Argument 列包含所使用的聚集索引名稱和 SEEK:() 謂詞。存儲引擎僅使用索引來處理滿足此 SEEK:() 謂詞的行。它還包括 WHERE:() 謂詞,其中存儲引擎對滿足 SEEK:() 謂詞的所有行進行計算,但此操作是可選的,並且不使用索引來完成此過程。

 

Table Scan 運算符從查詢執行計划的 Argument 列所指定的表中檢索所有行。如果 WHERE:()謂詞出現在 Argument 列中,則僅返回滿足此謂詞的那些行。

Filter 運算符掃描輸入,僅返回那些符合 Argument 列中的篩選表達式(謂詞)的行。

Inner Join 邏輯運算符返回滿足第一個(頂端)輸入與第二個(底端)輸入所組成的聯接的每一行。

SQL Server 2005 Service Pack 2 中引入的 Key Lookup 運算符是在具有聚集索引的表上進行的書簽查找。Argument 列包含聚集索引的名稱和用來在聚集索引中查找行的聚集鍵。

 

RID Lookup 是在使用提供的行標識符 (RID) 在堆上進行的書簽查找。Argument 列包含用於查找行的書簽標簽和從中查找行的表的名稱。RID

1. 有效地查詢參數

得到相同查詢結果的SQL語句的寫法有很多種,那么應該如何決定采用哪種SQL語句編寫方式比較有用呢?最重要的考慮因素之一是WHERE 條件子句, WHERE子句限制了查詢所要返問的記錄數量,查詢優化程序會嘗試判斷己有的索引,分析對查找符合WHERE子句條件的記錄是否有幫助。

查詢優化程序首先就要查看WHERE 子句中所有的條件,以決定這些條件在限制SQL SERVER 訪問數據時是否有用。換句話說,查詢子句是否有用要看查詢參數(Searchable Arguments , SARG〕 

很多人不知道SQL語句在SQL SERVER中是如何執行的,他們擔心自己所寫的SQL語句會被SQL SERVER誤解。比如: 
SELECT *  FROM [WBK_PDE_LIST_ORG_HISTROY] where QTY_1>53 and COP_G_NO='90206884'

  和執行
SELECT *

  FROM [WBK_PDE_LIST_ORG_HISTROY] where  COP_G_NO='90206884' and QTY_1>53 
一些人不知道以上兩條語句的執行效率是否一樣,因為如果簡單的從語句先后上看,這兩個語句的確是不一樣,如果QTY_1是一個非聚集索引,那么前一句僅僅從QTY_1大於53的記錄中查找就行了;而后一句則要先從全表中查找看有幾個COP_G_NO='90206884'的,而后再根據限制條件條件QTY_1>53來提出查詢結果。 

事實上,這樣的擔心是不必要的。SQL SERVER中有一個查詢分析優化器,它可以根據WHERE子句中的搜索條件進行自動優化,建立有效的索引使用計划。 

上面兩句的IO情況是一樣的,都是250次邏輯讀取操作。具體執行結果如下:

(61 行受影響)

'WBK_PDE_LIST_ORG_HISTROY'。掃描計數1,邏輯讀取250 次,物理讀取次,預讀次,lob 邏輯讀取次,lob 物理讀取次,lob 預讀次。

 

(61 行受影響)

'WBK_PDE_LIST_ORG_HISTROY'。掃描計數1,邏輯讀取250 次,物理讀取次,預讀次,lob 邏輯讀取次,lob 物理讀取次,lob 預讀次。

從Managemenet studio中可以看出上面兩句的查詢執行計划都是一樣的。如下圖。

 

所以上面兩句的執行效率是一樣的。

 

雖然查詢優化器可以根據WHERE子句自動的進行查詢優化,但大家仍然有必要了解一下查詢優化器的工作原理,我們有時會以查詢參數這個名詞來泛指在WHERE 子句中所有的條件,但此處使用SARG縮寫來代表查詢參數的有效格式。在大多數狀況下,查詢優化程序只能對符合SARG 條件的WHERE子句通過索引找到優化的執行方式。如果一個階段可以被用作一個掃描參數(SARG),那么就稱之為可優化的,並且可以利用索引快速獲得所需數據。 

 

SARG的定義:用於限制搜索的一個操作,因為它通常是指一個特定的匹配,一個值得范圍內的匹配或者兩個以上條件的AND連接。SARG 包含常量描述式(或是可以解析成常量的變量)來與數據表中的字段做比較。SARG 的格式是:

列名 操作符 <常數 或 變量

<常數 或 變量操作符 列名 

列名出現在操作符的一邊,而常量或變量出現在另一邊。如果列名同時出現在操作的兩邊就不算是SARG。

SARG包含以下操作符=、>、<、>=、<=、BETWEEN及部分情況下的LIKE。LIKE是否符合SARG,要看通配符%所在的位置。例如:LIKE '胡%'就是符合SARG,但是'%胡'就不符合SARG。因為以通配符開頭無法限制SQL SERVER查詢記錄的數量,索引的擺放依然是以小到大,或以大到小順序排列,如果以通配符“%”開頭就無法利用有序的結構,以二分法來快速查找數據。

簡言之,在查詢子句中,SARG代表用來查找的常量或變量可以直接與索引鍵值進行比較,下面是一些常用SARG與執行索引的關系。

 

 序號 索引
 SQL語句與查詢執行計划  記錄數  執行成本

1

索引4

SELECT [WBOOK_NO]      ,[COP_G_NO] ,[G_NO],[CODE_T]             

      ,[QTY_1],[UNIT_1],[TRADE_TOTAL]  

      ,[GROSS_WT] FROM [WBK_PDE_LIST_ORG_HISTROY] where qty_1=1

   
   

'WBK_PDE_LIST_ORG_HISTROY'。掃描計數1,邏輯讀取29 次,物理讀取次,預讀次,lob 邏輯讀取次,lob 物理讀取次,lob 預讀次。

   
     

29

0.0267688

2

索引1

SELECT * FROM [WBK_PDE_LIST_ORG_HISTROY] where qty_1=1

   
   

'WBK_PDE_LIST_ORG_HISTROY'。掃描計數1,邏輯讀取1314 次,物理讀取次,預讀次,lob 邏輯讀取次,lob 物理讀取次,lob 預讀次。

1314

1.03687

     

   

3

索引4

SELECT  [WBOOK_NO],[COP_G_NO],[G_NO],[CODE_T],[QTY_1]

      ,[UNIT_1],[TRADE_TOTAL]  

      ,[GROSS_WT] FROM [WBK_PDE_LIST_ORG_HISTROY] where qty_1>=312

   
   

'WBK_PDE_LIST_ORG_HISTROY'。掃描計數1,邏輯讀取29 次,物理讀取次,預讀次,lob 邏輯讀取次,lob 物理讀取次,lob 預讀次。

   
     

29

0.0268468

4

索引1

SELECT * FROM [WBK_PDE_LIST_ORG_HISTROY] where qty_1>=312

   
   

'WBK_PDE_LIST_ORG_HISTROY'。掃描計數1,邏輯讀取1314 次,物理讀取次,預讀次,lob 邏輯讀取次,lob 物理讀取次,lob 預讀次。

   
     

1314

1.03687

5

索引4

SELECT  [WBOOK_NO],[COP_G_NO],[G_NO],[CODE_T] 

      ,[QTY_1],[UNIT_1],[TRADE_TOTAL]  

      ,[GROSS_WT] FROM [WBK_PDE_LIST_ORG_HISTROY] where qty_1<2

   
   

'WBK_PDE_LIST_ORG_HISTROY'。掃描計數1,邏輯讀取29 次,物理讀取次,預讀次,lob 邏輯讀取次,lob 物理讀取次,lob 預讀次。

   
     

29

0.026875

6

索引1

SELECT * FROM [WBK_PDE_LIST_ORG_HISTROY] where qty_1<2

   
   

'WBK_PDE_LIST_ORG_HISTROY'。掃描計數1,邏輯讀取1314 次,物理讀取次,預讀次,lob 邏輯讀取次,lob 物理讀取次,lob 預讀次。

   
     

1314

1.03687

7

索引4

SELECT  [WBOOK_NO],[COP_G_NO] ,[G_NO],[CODE_T]             

      ,[QTY_1],[UNIT_1],[TRADE_TOTAL]  

      ,[GROSS_WT]  FROM [WBK_PDE_LIST_ORG_HISTROY] where qty_1!>1

   
   

'WBK_PDE_LIST_ORG_HISTROY'。掃描計數1,邏輯讀取29 次,物理讀取次,預讀次,lob 邏輯讀取次,lob 物理讀取次,lob 預讀次。

   
     

29

0.026875

8

索引1

SELECT  * FROM [WBK_PDE_LIST_ORG_HISTROY] where qty_1!>1

   
   

'WBK_PDE_LIST_ORG_HISTROY'。掃描計數1,邏輯讀取1314 次,物理讀取次,預讀次,lob 邏輯讀取次,lob 物理讀取次,lob 預讀次。

   
     

1314

1.03687

9

索引2

SELECT *

  FROM [WBK_PDE_LIST_ORG_HISTROY] where QTY_1 between 412 and 500

   
   

'WBK_PDE_LIST_ORG_HISTROY'。掃描計數1,邏輯讀取1021 次,物理讀取次,預讀次,lob 邏輯讀取次,lob 物理讀取次,lob 預讀次。

   
     

1021

0.959746

10

索引3

SELECT *

  FROM [WBK_PDE_LIST_ORG_HISTROY] where cop_g_no like '80215%'

   
   

'WBK_PDE_LIST_ORG_HISTROY'。掃描計數1,邏輯讀取320 次,物理讀取次,預讀次,lob 邏輯讀取次,lob 物理讀取次,lob 預讀次。

   
     

320

0.316824

11

索引3

SELECT *

  FROM [WBK_PDE_LIST_ORG_HISTROY] where cop_g_no like '802%'

SELECT *

  FROM [WBK_PDE_LIST_ORG_HISTROY] where cop_g_no like '%21%'

   
   

'WBK_PDE_LIST_ORG_HISTROY'。掃描計數1,邏輯讀取1314 次,物理讀取次,預讀次,lob 邏輯讀取次,lob 物理讀取次,lob 預讀次。

1314

1.03687

     

   

 

SQL SERVER查詢分析優化器對於每一條查詢語句的WHERE子句進行評估,看是使用索引的查詢成本還是使用聚集索引掃描的查詢成本低。

從上表中我們可以看出根據不同的查詢語句與不同的查詢字段,會使用不同的索引,如果當查詢出來的記錄數比較多時,也就是超過了直接使用聚集索引掃描或全表掃描查詢出來的數據時,即使WHERE子名是SARG格式的寫法,他也將使用放棄使用相應的索引,而使用全表掃描與聚集索引掃描(例如上表中的2,4,6,8,11)。

 

使用索引

查詢語句

查詢記錄數量

執行成本 

索引2

9

1021

0.959746

索引3

10

320

0.316824

索引4

1,3,5,7

29

0.026875

索引5

     

索引1

2,4,6,8,11

1314

1.03687


免責聲明!

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



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