サムが言うように、LIKE '[a-d]%'
SARGable (ほぼ) です。ほとんどの場合、最適化されていないためですPredicate
(詳細については、以下を参照してください)。
例 #1: このクエリをAdventureWorks2008R2データベースで実行する場合
SET STATISTICS IO ON;
SET NOCOUNT ON;
PRINT 'Example #1:';
SELECT p.BusinessEntityID, p.LastName
FROM Person.Person p
WHERE p.LastName LIKE '[a-a]%'
次に、演算子に基づいて実行計画を取得しますIndex Seek
(最適化された述語: 緑の四角形、最適化されていない述語: 赤の四角形):
の出力SET STATISTICS IO
は次のとおりです。
Example #1:
Table 'Person'. Scan count 1, logical reads 7
これは、サーバーがバッファー プールから 7 ページを読み取る必要があることを意味します。また、この場合、インデックスには、および句でIX_Person_LastName_FirstName_MiddleName
必要なすべての列が含まれます: LastName および BusinessEntityID。テーブルにクラスター化インデックスがある場合、すべての非クラスター化インデックスにはクラスター化インデックス キーの列が含まれます (BusinessEntityID は PK_Person_BusinessEntityID クラスター化インデックスのキーです)。SELECT
FROM
WHERE
しかし:
1)次の理由により、クエリはすべての列を表示する必要がありますSELECT *
(これは悪い習慣です):BusinessEntityID、LastName、FirstName、MiddleName、PersonType、...、ModifiedDate。
2) インデックス (IX_Person_LastName_FirstName_MiddleName
前の例) には、必要なすべての列が含まれていません。これが、このクエリの場合、このインデックスが非カバー インデックスである理由です。
ここで、次のクエリを実行すると、diff が取得されます。[実際の] 実行計画 (SSMS、Ctrl + M):
SET STATISTICS IO ON;
SET NOCOUNT ON;
PRINT 'Example #2:';
SELECT p.*
FROM Person.Person p
WHERE p.LastName LIKE '[a-a]%';
PRINT @@ROWCOUNT;
PRINT 'Example #3:';
SELECT p.*
FROM Person.Person p
WHERE p.LastName LIKE '[a-z]%';
PRINT @@ROWCOUNT;
PRINT 'Example #4:';
SELECT p.*
FROM Person.Person p WITH(FORCESEEK)
WHERE p.LastName LIKE '[a-z]%';
PRINT @@ROWCOUNT;
結果:
Example #2:
Table 'Person'. Scan count 1, logical reads 2805, lob logical reads 0
911
Example #3:
Table 'Person'. Scan count 1, logical reads 3817, lob logical reads 0
19972
Example #4:
Table 'Person'. Scan count 1, logical reads 61278, lob logical reads 0
19972
実行計画:
さらに、クエリは、「Person.Person」で作成されたすべてのインデックスのページ数を示します。
SELECT i.name, i.type_desc,f.alloc_unit_type_desc, f.page_count, f.index_level FROM sys.dm_db_index_physical_stats(
DB_ID(), OBJECT_ID('Person.Person'),
DEFAULT, DEFAULT, 'DETAILED' ) f
INNER JOIN sys.indexes i ON f.object_id = i.object_id AND f.index_id = i.index_id
ORDER BY i.type
name type_desc alloc_unit_type_desc page_count index_level
--------------------------------------- ------------ -------------------- ---------- -----------
PK_Person_BusinessEntityID CLUSTERED IN_ROW_DATA 3808 0
PK_Person_BusinessEntityID CLUSTERED IN_ROW_DATA 7 1
PK_Person_BusinessEntityID CLUSTERED IN_ROW_DATA 1 2
PK_Person_BusinessEntityID CLUSTERED ROW_OVERFLOW_DATA 1 0
PK_Person_BusinessEntityID CLUSTERED LOB_DATA 1 0
IX_Person_LastName_FirstName_MiddleName NONCLUSTERED IN_ROW_DATA 103 0
IX_Person_LastName_FirstName_MiddleName NONCLUSTERED IN_ROW_DATA 1 1
...
Example #1
ここで、とExample #2
(両方とも 911 行を返す)を比較すると、
`SELECT p.BusinessEntityID, p.LastName ... p.LastName LIKE '[a-a]%'`
vs.
`SELECT * ... p.LastName LIKE '[a-a]%'`
次に、2 つの差分が表示されます。
a) 7 つの論理読み取りと 2805 の論理読み取り
b) Index Seek
(#1) 対Index Seek
+ Key Lookup
(#2)。
(#2) クエリのパフォーマンスがはるかに悪いことがわかりSELECT *
ます (7 ページ対 2805 ページ)。
ここで、 を比較するExample #3
とExample #4
(どちらも 19972 行を返します)
`SELECT * ... LIKE '[a-z]%`
vs.
`SELECT * ... WITH(FORCESEEK) LIKE '[a-z]%`
次に、2 つの差分が表示されます。
a) 3817 の論理読み取り (#3) 対 61278 の論理読み取り (#4) および
b) Clustered Index Scan
(PK_Person_BusinessEntityID には 3808 + 7 + 1 + 1 + 1 = 3818 ページあります) vs. Index Seek
+ Key Lookup
.
Index Seek
+ (#4) クエリのパフォーマンスがはるかに悪いことがわかりKey Lookup
ます (3817 ページ対 61278 ページ)。この場合、and Index Seek
onIX_Person_LastName_FirstName_MiddleName
とKey Lookup
on PK_Person_BusinessEntityID
(クラスター化インデックス) を使用すると、「クラスター化インデックス スキャン」よりもパフォーマンスが低下することがわかります。
そして、これらすべての悪い実行計画は、SELECT *
.