你的位置:首页 > 数据库

[数据库]索引扫描与索引查找


    扫描与查找操作均是SQL Server从表或索引中读取数据采用的迭代器,这些也是SQL Server支持的最基本的运算。几乎在每一个查询计划中都可以找到,因此理解它们的不同是很重要的,扫描是在整张表上进行处理,而索引是在整个页级上进行处理,而查找则返回特定谓词上一个或多个范围内的数据行。

   下面让我们看一个扫描的例子

   SELECT[OrderId]FROM[Orders]WHERE[RequiedDate]='2015-03-21'

    在Orders表中,并不存在对RequiredDate列的索引,因此,SQL Server必须读取Orders表的每一行来股几每一行的RequiredDate谓词,如果满足该谓词条件,则返回该行数据。

   为了最大化的提升性能,SQL Server尽可能地使用扫描迭代器来估计该谓词,然而,如果该谓词过于复杂或开销过大,SQL Server或许使用别的筛选迭代器来估计。以下是WHERE关键字中的文本计划的过程:  

|--Clustered Index Scan(OBJECT:([Orders].[PK_Orders]),
WHERE:([Orders].[RequiredDate]='1998-03-26'))

下图描述了该操作的流程图:

    由于扫描表的每一行数据,不论满足与否,因此,其查询开销对表中的总记录数是均衡的,当表中的数据很少或满足谓词的行比较多时,采用扫描操作有效,如果表中数据量比较大或满足谓词的行较少时,使用扫描将读取更多的页面或执行更多的I/O操作来获取数据,这显而不是最有效的方法.

下面让我们看一个关于索引查找的例子,下面的例子在OrderdDate列上创建了索引:     SELECT [OrderId] FROM [Orders] WHERE [OrderDate] = '1998-02-26'

    这次SQL Server能够使用索引查找来直接找到满足谓词的那些记录行,这里称该谓词为"查找"谓词.大多数情况下,SQL Server并不显式地估计"查找"谓词,而索引确保了"查找"操作仅返回满足的数据行,以下是"查找"谓词的文本计划:

|--Index Seek(OBJECT:([Orders].[OrderDate]),

SEEK:([Orders].[OrderDate]=CONVERT_IMPLICIT(datetime,[@1],0)) ORDERED FORWARD

注意:SQL Server自动使用@1参数替换查询文本中的参数

由此看来,查找仅扫描满足该谓词的数据页,其查询开销显然要比表中总记录数的开销低,因此,对于高选择度的查询谓词操作,查找通常是最有效的策略.也就是说,对于估计大表中的数据时,使用查找谓词是比较有效率的.

SQL Server将扫描与查找进行区分,如同将在堆(无聚集索引的对象)上扫描,聚集索引上的扫描,非聚集索引上的扫描进行分区.下表说明了这些出现在的查询计划中的扫描与查找运算.

   
   
   

 

 

扫描

查找

表扫描

 

聚集索引

聚集索引找描

聚集索引查找

非聚集索引

索引扫描

索引查找

 

 

 

 

 

 

可查找的谓词与覆盖列

         SQL Server在执行索引查找之前,它需要确定索引的键是否满足查询中的谓词,我们称该谓词为"可查找的谓词",SQL Server必须确定该索引是否包含或"覆盖"查询中引用的列集合.下面描述了如何确定哪个谓词是可查找的,哪个谓词不是可查找的,哪些列需要索引覆盖.

单列索引

         在单列索引上判断谓词是否是可查找的是很容易的,SQL Server使用单列索引来响应多数简单的比较(包括相等和不等(大于,小于等))或者更复杂的表达式,如在列上运算的函数和LIKE %谓词,这些运算符将阻止SQL Server使用索引查找.

例如,假设我们在Col1列上创建了单列索引,可以在以下谓词上进行索引查找:

  • [Col1] = 3.14
  • [Col1] > 100
  • [Col1] BETWEEN 0 AND 99
  • [Col1] LIKE 'abc%'
  • [Col1] IN (2, 3, 5, 7)

然页,在以下谓词上将不能使用索引查找:

  • ABS([Col1]) = 1
  • [Col1] + 1 = 9
  • [Col1] LIKE '%abc'
  • 下面我通过一些例子来介绍单列索引:

首先创建一些架构对象: create table person (id int, last_name varchar(30), first_name varchar(30))

create unique clustered index person_id on person (id) create index person_name on person (last_name, first_name)

 

以下是三个查询及其各自的文本查询计划,第一个查询在person_name索引上进行查找,第二个查询首先在第一个键列上进行索引查找,然后使用residual谓词来估计first_name,第三个查询不能使用索引查找,而是使用了索引扫描来处理residual谓词.

select id from person where last_name = 'Doe' and first_name = 'John'

|--Index Seek(OBJECT:([person].[person_name]), SEEK:([person].[last_name]='Doe' AND [person].[first_name]='John'))

 

select id from person where last_name > 'Doe' and first_name = 'John'

|--Index Seek(OBJECT:([person].[person_name]), SEEK:([person].[last_name] > 'Doe'),  WHERE:([person].[first_name]='John'))

 

select id from person where last_name like '%oe' and first_name = 'John'

  |--Index Scan(OBJECT:([person].[person_name]),  WHERE:([person].[first_name]='John' AND [person].[last_name] like '%oe'))

上面三条查询的图形查询计划: