小迪的Blog
学习ing...(注明:该Blog中的任何信息都非原创,只是作为个人的阅读笔记)
posts - 144,comments - 52,trackbacks - 0

关于索引,我们可以知道的更多 --全表扫描和索引扫描

先建一个简单的表结构

create table test(a int ,b datetime
create index idx_b on test(b) 

 

再来点测试数据

declare @i int 
declare @x int 
set @i = 0 
set @x = 0 
while (@i < 1000
begin 
insert into test values(@i,dateadd(hour,rand(@x) * 10000,getdate())) 
set @x = @x + rand() * 1000 
set @i = @i + 1 
end 


这样的两句查询:

select a from test 
select 1 from test 

可以看到前者使用全表扫描,后者使用了索引,这样一来实在是很迷惑,SQLServer为什么要对后者使用索引扫描呢?又没有过滤条件。
其实是有原因的,因为SqlServer知道后者不会取表数据,那么对于索引树的扫描将要快于表的扫描(其实事实上未必如此),因为索引树占用的空间一般要小于表数据的空间,而小的数据读取可以减小I/O读取,要知道这是最耗时的一个操作,但实际上呢,因为当前索引使用的是占数据行空间绝大多数的b字段,所以在这里,第二条语句要慢于第一条。

如果不信,那么继续,先对表加一个a字段的非聚集索引:

create index idx_a on test(a) 

然后再执行上面两个查询,这次SQLServer很聪明地选择了占用空间更小的idx_a索引扫描来对第二句查询进行执行,但是等等,怎么还是第一个查询执行得更快???其实原因很简单,对索引树的遍历比对表空间的遍历要更复杂,因为索引树用中间节点来存放数据(大家看看B树的结构就明白了,SQLServer使用的是B-树),只有叶子节点才指向具体的数据(这个指向会因为有没有聚集索引而不同,这个以后会讲到),因此第一句的全表扫描要快一些,这是不是表示SQLServer选择了错误的扫描方式呢?非也,现在试着把数据加大到10000条,再执行,看到了吧,索引扫描要快(此处忽略对字段a的select操作成本,因为此成本开销实在是非常小)。


再来执行两条SQL语句:

select * from test where b is null 
select * from test where b is not null 

可以看到,这两个查询都利用了索引,在此更正我上一篇BLOG中的错误,SQLServer是会将NULL值存入索引树的,这和它的数据结构有关,通过NULL位图,它可以处理NULL值,而索引叶子节点中的数据存放结构和表数据行的结构是大同小异的,而Oracle是不会存放NULL到索引的。

好了,先吃饭了,接下来的东西以后再讲。


引用自:
http://www.cnblogs.com/progame/archive/2005/01/14/91780.html

posted on 2005-04-16 14:02  小迪  阅读(...)  评论(...编辑  收藏