like語句百分号前置會使用到索引嗎?
前幾天看了這篇文章:談SQL Server對like '%關鍵詞%' 處理時的索引利用問題
看完了之後,我很想知道這篇文章是不是臨時工寫的?還是網站的主人寫的,網站的主人的微網誌我都有關注(在微網誌裡私信過)
是某個公司的DBA,這裡先不管他是不是臨時工寫的,今天我也研究一下這個問題o(∩_∩)o
說明:我們說的走索引指的是:聚集索引查找、非聚集索引查找
而全表掃描、聚集索引掃描、非聚集索引掃描都不是走索引
而這裡說的走索引跟全文搜尋/全文索引沒有關系 SQLSERVER全文搜尋
全文搜尋/全文索引已經是另外一種技術了
聚集索引表
SQL腳本如下:
![](https://img.laitimes.com/img/9ZDMuAjOiMmIsIjOiQnIsIiZpdmLrN2bsJEZlR3YhJHdu92Qvw1cy9GdhNWak5WSn5WaulGb0V3TvwVbvNmLzd2bsJmbj5ycldWYtl2Lc9CX6MHc0RHaiojIsJye.gif)
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和時間統計
![](https://img.laitimes.com/img/9ZDMuAjOiMmIsIjOiQnIsIiZpdmLrN2bsJEZlR3YhJHdu92Qvw1cy9GdhNWak5WSn5WaulGb0V3TvwVbvNmLzd2bsJmbj5ycldWYtl2Lc9CX6MHc0RHaiojIsJye.gif)
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%'用到了查找
為什麽?我會在文中最後說明
非聚集索引表
我們drop掉剛才的department表
1 DROP TABLE [dbo].[Department]
重建立立department表
![](https://img.laitimes.com/img/9ZDMuAjOiMmIsIjOiQnIsIiZpdmLrN2bsJEZlR3YhJHdu92Qvw1cy9GdhNWak5WSn5WaulGb0V3TvwVbvNmLzd2bsJmbj5ycldWYtl2Lc9CX6MHc0RHaiojIsJye.gif)
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
非聚集索引依然建立在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查找
這裡的結果跟聚集索引是一樣的
-------------------------------------------------------------------------------------------
![](https://img.laitimes.com/img/9ZDMuAjOiMmIsIjOiQnIsIiZpdmLrN2bsJEZlR3YhJHdu92Qvw1cy9GdhNWak5WSn5WaulGb0V3TvwVbvNmLzd2bsJmbj5ycldWYtl2Lc9CX6MHc0RHaiojIsJye.gif)
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
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