先建立一个简单的表结构

关于索引,我们可以多知道一些 -- 全表扫描和索引扫描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到索引的。

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

相关文章:

  • 2021-08-27
  • 2022-12-23
  • 2022-12-23
  • 2021-10-16
  • 2022-12-23
  • 2021-11-29
  • 2021-08-11
  • 2021-06-16
猜你喜欢
  • 2022-01-29
  • 2021-11-28
  • 2022-12-23
  • 2021-10-09
  • 2022-12-23
  • 2022-02-16
  • 2022-12-23
相关资源
相似解决方案