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

先建立一个简单的表结构

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到索引的。

好了,先吃饭了,接下来的东西以后再讲。
posted @ 2005-01-14 11:40  progame  阅读(3270)  评论(3编辑  收藏  举报