like語句百分號前置會使用到索引嗎?


like語句百分號前置會使用到索引嗎?


前幾天看了這篇文章:談SQL Server對like '%關鍵詞%' 處理時的索引利用問題

看完了之后,我很想知道這篇文章是不是臨時工寫的?還是網站的主人寫的,網站的主人的微博我都有關注(在微博里私信過)

是某個公司的DBA,這里先不管他是不是臨時工寫的,今天我也研究一下這個問題o(∩_∩)o

 

說明:我們說的走索引指的是:聚集索引查找、非聚集索引查找

而全表掃描、聚集索引掃描、非聚集索引掃描都不是走索引

 

而這里說的走索引跟全文搜索/全文索引沒有關系  SQLSERVER全文搜索

全文搜索/全文索引已經是另外一種技術了


聚集索引表
SQL腳本如下:

 1 --聚集索引表
 2 USE [pratice]
 3 GO
 4 CREATE TABLE Department(
 5     DepartmentID int IDENTITY(1,1) NOT NULL ,
 6     GroupName NVARCHAR(20) NOT NULL,
 7     Company NVARCHAR(20),
 8 )
 9 
10 CREATE CLUSTERED INDEX CL_GroupName ON [dbo].[Department](GroupName ASC)
11 
12 DECLARE @i INT
13 SET @i=1
14 WHILE @i < 100000 
15     BEGIN
16         INSERT  INTO Department (  [Company], groupname )
17         VALUES  (  '中國你好有限公司XX分公司'+CAST(@i AS VARCHAR(20)), '銷售組'+CAST(@i AS VARCHAR(20)) )
18         SET @i = @i + 1
19     END
20 
21 
22 SELECT * FROM Department
View Code

表數據


聚集索引創建在GROUPNAME這個字段上,我們就查找GROUPNAME這個字段

如果看過我以前寫的文章的人肯定知道:查找只會出現在建立索引的時候的第一個字段(這里我就不再詳細敘述了)

1 --聚集索引查找
2 SELECT * FROM [dbo].[Department] WHERE [GroupName] LIKE '銷售組1000'
3 --聚集索引掃描
4 SELECT * FROM [dbo].[Department] WHERE [GroupName] LIKE '%銷售組1000%'
5 --聚集索引掃描
6 SELECT * FROM [dbo].[Department] WHERE [GroupName] LIKE '%銷售組1000'
7 --聚集索引查找
8 SELECT * FROM [dbo].[Department] WHERE [GroupName] LIKE '銷售組1000%'

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

IO和時間統計

 1 SET STATISTICS IO ON
 2 SET STATISTICS TIME ON
 3 SELECT * FROM [dbo].[Department] WHERE [GroupName] LIKE '銷售組1000'
 4 SET STATISTICS IO OFF
 5 SET STATISTICS TIME OFF
 6 
 7 SET STATISTICS IO ON
 8 SET STATISTICS TIME ON
 9 SELECT * FROM [dbo].[Department] WHERE [GroupName] LIKE '%銷售組1000%'
10 SET STATISTICS IO OFF
11 SET STATISTICS TIME OFF
12 
13 SET STATISTICS IO ON
14 SET STATISTICS TIME ON
15 SELECT * FROM [dbo].[Department] WHERE [GroupName] LIKE '%銷售組1000'
16 SET STATISTICS IO OFF
17 SET STATISTICS TIME OFF
18 
19 SET STATISTICS IO ON
20 SET STATISTICS TIME ON
21 SELECT * FROM [dbo].[Department] WHERE [GroupName] LIKE '銷售組1000%'
22 SET STATISTICS IO OFF
23 SET STATISTICS TIME OFF
View Code
 1 (1 行受影響)LIKE '銷售組1000'
 2'Department'。掃描計數 1,邏輯讀取 3 次,物理讀取 0 次,預讀 0 次,lob 邏輯讀取 0 次,lob 物理讀取 0 次,lob 預讀 0 次。
 3 
 4 SQL Server 執行時間:
 5    CPU 時間 = 0 毫秒,占用時間 = 0 毫秒。
 6 
 7 SQL Server 執行時間:
 8    CPU 時間 = 0 毫秒,占用時間 = 0 毫秒。
 9 
10 (11 行受影響)LIKE '%銷售組1000%'
11'Department'。掃描計數 1,邏輯讀取 448 次,物理讀取 0 次,預讀 0 次,lob 邏輯讀取 0 次,lob 物理讀取 0 次,lob 預讀 0 次。
12 
13 SQL Server 執行時間:
14    CPU 時間 = 47 毫秒,占用時間 = 47 毫秒。
15 
16 SQL Server 執行時間:
17    CPU 時間 = 0 毫秒,占用時間 = 0 毫秒。
18 
19 (1 行受影響)LIKE '%銷售組1000'
20'Department'。掃描計數 1,邏輯讀取 448 次,物理讀取 0 次,預讀 0 次,lob 邏輯讀取 0 次,lob 物理讀取 0 次,lob 預讀 0 次。
21 
22 SQL Server 執行時間:
23    CPU 時間 = 47 毫秒,占用時間 = 40 毫秒。
24 
25 SQL Server 執行時間:
26    CPU 時間 = 0 毫秒,占用時間 = 0 毫秒。
27 
28 (11 行受影響)LIKE '銷售組1000%'
29'Department'。掃描計數 1,邏輯讀取 3 次,物理讀取 0 次,預讀 0 次,lob 邏輯讀取 0 次,lob 物理讀取 0 次,lob 預讀 0 次。
30 
31 SQL Server 執行時間:
32    CPU 時間 = 0 毫秒,占用時間 = 0 毫秒。
33 
34 SQL Server 執行時間:
35    CPU 時間 = 0 毫秒,占用時間 = 0 毫秒。

 

只有LIKE '銷售組1000'LIKE '銷售組1000%'用到了查找

為什麽?我會在文中最后說明


非聚集索引表

SQL腳本如下:

我們drop掉剛才的department表

1 DROP TABLE [dbo].[Department]

重新建立department表

 1 --非聚集索引表
 2 USE [pratice]
 3 GO
 4 CREATE TABLE Department(
 5     DepartmentID int IDENTITY(1,1) NOT NULL ,
 6     GroupName NVARCHAR(20) NOT NULL,
 7     Company NVARCHAR(20),
 8 )
 9 
10 CREATE  INDEX CL_GroupName ON [dbo].[Department](GroupName ASC)
11 
12 DECLARE @i INT
13 SET @i=1
14 WHILE @i < 100000 
15     BEGIN
16         INSERT  INTO Department (  [Company], groupname )
17         VALUES  (  '中國你好有限公司XX分公司'+CAST(@i AS VARCHAR(20)), '銷售組'+CAST(@i AS VARCHAR(20)) )
18         SET @i = @i + 1
19     END
20 
21 
22 SELECT * FROM Department
View Code

非聚集索引依然建立在GROUPNAME這個字段上

 

表數據


同樣,我們使用上面講解聚集索引表時候的查詢語句

1 --非聚集索引查找  RID查找
2 SELECT * FROM [dbo].[Department] WHERE [GroupName] LIKE '銷售組1000'
3 --非聚集索引掃描  RID查找
4 SELECT * FROM [dbo].[Department] WHERE [GroupName] LIKE '%銷售組1000%'
5 --非聚集索引掃描   RID查找
6 SELECT * FROM [dbo].[Department] WHERE [GroupName] LIKE '%銷售組1000'
7 --非聚集索引查找    RID查找
8 SELECT * FROM [dbo].[Department] WHERE [GroupName] LIKE '銷售組1000%'

因為是select * 所以SQLSERVER需要到數據頁面去找其他的字段數據,使用到RID查找

這里的結果跟聚集索引是一樣的

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

IO和時間統計

 1 SET STATISTICS IO ON
 2 SET STATISTICS TIME ON
 3 SELECT * FROM [dbo].[Department] WHERE [GroupName] LIKE '銷售組1000'
 4 SET STATISTICS IO OFF
 5 SET STATISTICS TIME OFF
 6 
 7 SET STATISTICS IO ON
 8 SET STATISTICS TIME ON
 9 SELECT * FROM [dbo].[Department] WHERE [GroupName] LIKE '%銷售組1000%'
10 SET STATISTICS IO OFF
11 SET STATISTICS TIME OFF
12 
13 SET STATISTICS IO ON
14 SET STATISTICS TIME ON
15 SELECT * FROM [dbo].[Department] WHERE [GroupName] LIKE '%銷售組1000'
16 SET STATISTICS IO OFF
17 SET STATISTICS TIME OFF
18 
19 SET STATISTICS IO ON
20 SET STATISTICS TIME ON
21 SELECT * FROM [dbo].[Department] WHERE [GroupName] LIKE '銷售組1000%'
22 SET STATISTICS IO OFF
23 SET STATISTICS TIME OFF
View Code
 1 (1 行受影響) LIKE '銷售組1000'
 2'Department'。掃描計數 1,邏輯讀取 3 次,物理讀取 0 次,預讀 0 次,lob 邏輯讀取 0 次,lob 物理讀取 0 次,lob 預讀 0 次。
 3 
 4 SQL Server 執行時間:
 5    CPU 時間 = 0 毫秒,占用時間 = 62 毫秒。
 6 
 7 SQL Server 執行時間:
 8    CPU 時間 = 0 毫秒,占用時間 = 0 毫秒。
 9 
10 (11 行受影響)LIKE '%銷售組1000%'
11'Department'。掃描計數 1,邏輯讀取 92 次,物理讀取 0 次,預讀 0 次,lob 邏輯讀取 0 次,lob 物理讀取 0 次,lob 預讀 0 次。
12 
13 SQL Server 執行時間:
14    CPU 時間 = 16 毫秒,占用時間 = 17 毫秒。
15 
16 SQL Server 執行時間:
17    CPU 時間 = 0 毫秒,占用時間 = 0 毫秒。
18 
19 (1 行受影響)LIKE '%銷售組1000'
20'Department'。掃描計數 1,邏輯讀取 82 次,物理讀取 0 次,預讀 0 次,lob 邏輯讀取 0 次,lob 物理讀取 0 次,lob 預讀 0 次。
21 
22 SQL Server 執行時間:
23    CPU 時間 = 15 毫秒,占用時間 = 17 毫秒。
24 
25 SQL Server 執行時間:
26    CPU 時間 = 0 毫秒,占用時間 = 0 毫秒。
27 
28 (11 行受影響)LIKE '銷售組1000%'
29'Department'。掃描計數 1,邏輯讀取 13 次,物理讀取 0 次,預讀 0 次,lob 邏輯讀取 0 次,lob 物理讀取 0 次,lob 預讀 0 次。
30 
31 SQL Server 執行時間:
32    CPU 時間 = 0 毫秒,占用時間 = 0 毫秒。
33 
34 SQL Server 執行時間:
35    CPU 時間 = 0 毫秒,占用時間 = 0 毫秒。

 


為什麽只有LIKE '銷售組1000'LIKE '銷售組1000%'用到了查找???

如果閣下曾經有看過我寫的

SQLSERVER聚集索引與非聚集索引的再次研究(上)
SQLSERVER聚集索引與非聚集索引的再次研究(下)

您就會知道在聚集索引頁面和非聚集索引頁面里都有一個KeyHashValue的字段

聚集索引頁面

非聚集索引頁面

 

當使用 '%銷售組1000%'和'%銷售組1000'的時候,因為是模糊匹配(百分號前置)

SQLSERVER不會去匹配hash值(KeyHashValue),直接掃描(SCAN)算了

但是使用'銷售組1000'和'銷售組1000%'的時候

'銷售組1000' :SQLSERVER能夠准確匹配到唯一的一個hash值

'銷售組1000%':SQLSERVER會匹配與銷售組1000相同的hash值

銷售組1000%匹配的記錄會有多個,所以邏輯讀取次數也會有多次

所以,'銷售組1000'和'銷售組1000%'能夠使用查找(SEEK)


總結

只有了解了SQLSERVER的內部原理,才能夠明白更多

 

注意:我這里並沒有將非聚集索引掃描納入到“走索引”這個分類,如果將非聚集索引掃描納入到“走索引”這個分類里

那么我的朋友的文章就是對的,隨便加個非聚集索引,讓表掃描/聚集索引掃描變成非聚集索引掃描,就認為是走索引

(雖然非聚集索引掃描比聚集索引掃描/表掃描快,IO少)

那么下面四個語句都是屬於走索引,沒有什么好討論的,我們討論的前提是在基礎表上不加任何東西,如果在做實驗的過程中

隨便加個非聚集索引,然后走非聚集索引掃描就說走索引,那么這篇文章就沒有意義了,經過再三斟酌,

我決定將“非聚集索引掃描”移出“走索引”這個分類,畢竟查找(SEEK)比掃描(SCAN)快


SELECT * FROM [dbo].[Department] WHERE [GroupName] LIKE '銷售組1000'
SELECT * FROM [dbo].[Department] WHERE [GroupName] LIKE '%銷售組1000%'
SELECT * FROM [dbo].[Department] WHERE [GroupName] LIKE '%銷售組1000'
SELECT * FROM [dbo].[Department] WHERE [GroupName] LIKE '銷售組1000%'

 

最最后,補充說一下

我們判斷一個執行計划的性能的好壞的標准是什么??

就是哪個執行計划的邏輯讀次數最少

Logical reads:包含該語句從內存數據緩沖區中訪問的頁數和從物理磁盤讀取的頁數。

如果全表掃描/聚集索引掃描所使用的邏輯讀比聚集索引查找/非聚集索引查找使用的邏輯讀要少,

或者全表掃描比非聚集索引掃描使用的邏輯讀要少

那么SQLSERVER選擇全表掃描/聚集索引掃描這個執行計划就是好的

有些人為了讓SQLSERVER使用索引,不惜代價使用查詢提示,讓SQLSRVER去走索引,這樣是得不償失的

我們的最終目的是:減少邏輯讀次數,不要為了索引而索引!!

當然,我這里的實驗環境跟各位的真實環境會有差別,不過“邏輯讀次數”這個標准無論是哪個環境都是一樣的!!

我說完了,謝謝大家o(∩_∩)o

 

physical reads:表示那些沒有駐留在內存緩沖區中需要從磁盤讀取的數據頁。
Read-ahead reads是SQL Server為了提高性能而產生的預讀。

如有不對的地方,歡迎大家拍磚o(∩_∩)o


免責聲明!

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



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