查询和索引以下内容的最有效方法是什么:
SELECT * Persons.LastName A-D
SELECT * Persons.LastName E-L
SELECT * Persons.LastName M-R
SELECT * Persons.LastName S-Z
我正在使用以下非常低效且难以索引的内容:
WHERE LastName LIKE '[a-d]%'
有关更好的方法的任何想法吗?我认为对于Filtered Index来说这可能是一个很好的场景,但是where子句需要更加可靠。
由于
答案 0 :(得分:2)
正如Sam所说,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
包括SELECT
,FROM
和WHERE
子句所需的所有列:LastName和BusinessEntityID。如果table具有聚簇索引,则所有非聚簇索引将包括聚簇索引键中的列(BusinessEntityID是PK_Person_BusinessEntityID聚簇索引的键)。
可是:
1)您的查询必须显示所有列,因为SELECT *
(它是错误的实践):BusinessEntityID,LastName,FirstName,MiddleName,PersonType,...,ModifiedDate。
2)索引(前一个示例中的IX_Person_LastName_FirstName_MiddleName
)不包括所有必需的列。这就是为什么对于此查询,此索引是非覆盖索引的原因。
现在,如果您执行下一个查询,那么您将获得差异。 [实际]执行计划(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
执行计划:
Plus:查询将为您提供在'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]%'`
然后你会看到两个差异:
a)7个逻辑读取与2805个逻辑读取和
b)Index Seek
(#1)与Index Seek
+ Key Lookup
(#2)。
您可以看到SELECT *
(#2)查询的效果最差(7页与2805页)。
现在,如果您比较Example #3
和Example #4
(两者都返回19972行)
`SELECT * ... LIKE '[a-z]%`
vs.
`SELECT * ... WITH(FORCESEEK) LIKE '[a-z]%`
然后你会看到两个差异:
a)3817个逻辑读取(#3)与61278个逻辑读取(#4)和
b)Clustered Index Scan
(PK_Person_BusinessEntityID有3808 + 7 + 1 + 1 + 1 = 3818页)与Index Seek
+ Key Lookup
。
您可以看到Index Seek
+ Key Lookup
(#4)查询的效果最差(3817页与61278页)。在这种情况下,您可以看到Index Seek
上的IX_Person_LastName_FirstName_MiddleName
加上Key Lookup
上的PK_Person_BusinessEntityID
(聚集索引)会使您的性能低于“聚簇索引扫描”。
由于SELECT *
,所有这些糟糕的执行计划都是可能的。
答案 1 :(得分:1)
你的谓词是可以理解的。
如果您在索引字段上运行此查询:
SELECT *
FROM persons
WHERE last_name >= 'a'
AND last_name < 'e'
它产生以下计划:
|--Nested Loops(Inner Join, OUTER REFERENCES:([MYDB].[dbo].[PERSONS].[ID]) OPTIMIZED)
|--Index Seek(OBJECT:([MYDB].[dbo].[PERSONS].[IX_PERSONS_LAST_NAME]), SEEK:([MYDB].[dbo].[PERSONS].[LAST_NAME] >= 'a' AND [MYDB].[dbo].[PERSONS].[LAST_NAME] < 'E'), WHERE:([MYDB].[dbo].[PERSONS].[LAST_NAME] like '[a-d]%') ORDERED FORWARD)
|--Clustered Index Seek(OBJECT:([MYDB].[dbo].[PERSONS].[IX_PERSONS_LAST_NAME]), SEEK:([MYDB].[dbo].[PERSONS].[ID]=[MYDB].[dbo].[PERSONS].[ID]) LOOKUP ORDERED FORWARD)
相当于运行此查询:
SELECT *
FROM persons
WHERE last_name >= 'a'
AND last_name < 'e'
答案 2 :(得分:1)
我会查看你的解释计划并启用STATISTICS IO和STATISTICS时间,看看是否有任何事情发生在你面前。