1

查询和索引以下内容的最有效方法是什么:

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]%'

关于更好的解决方法的任何想法?我认为这对于过滤索引来说可能是一个很好的场景,但是 where 子句需要更加可搜索。

谢谢

4

3 回答 3

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包括和子句所需的所有列:LastName 和 BusinessEntityID。如果表具有聚集索引,则所有非聚集索引都将包含聚集索引键中的列(BusinessEntityID 是 PK_Person_BusinessEntityID 聚集索引的键)。SELECTFROMWHERE

但:

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

执行计划: 在此处输入图像描述

另外:查询将为您提供在“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 #1and 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 #3and 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 页)。在这种情况下,您可以看到 and Index SeekonIX_Person_LastName_FirstName_MiddleName加上Key Lookupon PK_Person_BusinessEntityID(聚集索引)会给您带来比“聚集索引扫描”更低的性能。

所有这些糟糕的执行计划都是可能的,因为SELECT *.

于 2013-04-18T20:12:34.447 回答
1

你的谓词是sargable。

如果在索引字段上运行此查询:

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'
于 2013-04-18T16:46:49.273 回答
1

我会查看您的解释计划并打开 STATISTICS IO 和 STATISTICS 时间,看看是否有什么突然出现在您身上。

于 2013-04-18T17:04:54.587 回答