[转帖]SQL Server可否在Bit字段上建索引及其效率

  一直以来都认为在 bit 列上不能建立索引,因为 SQL Server 2000 的帮助文档在描述 bit 数据类型时,就是这样强调的:

 bit 
Integer data type 1, 0, or NULL.

Remarks
Columns of type bit cannot have indexes on them.


  在 SQL Server 2005 的帮助文档中,在描述数据类型 bit 时,去掉了不能建立索引的说明。

 bit 
An integer data type that can take a value of 1, 0, or NULL.

Remarks
The string values TRUE and FALSE can be converted to bit values:

TRUE is converted to 1 and FALSE is converted to 0.

在 SQL Server 2000 和 SQL Server 2005 中对 bit 的描述中一致的部分是: Microsoft SQL Server optimizes the storage used for bit columns. If there are 8 or fewer bit columns in a table,the columns are stored as 1 byte. If there are from 9 through 16 bit columns, they are stored as 2 bytes, and so on.

那么究竟能不能在 SQL Server 的 bit 列上建立索引?如果能的话,SQL Server 2005 和 SQL Server 2000 中 bit 索引 有何不同?是否需要用 tinyint 索引来代替 bit索引以提高性能?带着这些疑问,我做了下面的实验。

一、在 SQL Server 2005 中测试 bit 索引和 tinyint 索引。

use tempdb
go

-- 建立表 obj
select * into dbo.obj from master.sys.all_objects
go

-- 增加主键 id
alter table obj add id int null
go

declare @id int
set @id = 0
update obj set @id=id=@id+1 -- 为字段 id 赋递增值
go

alter table obj alter column id int not null
go

alter table obj add constraint pk_obj primary key (id)
go

-- 增加 bit 型字段 bitcol
alter table obj add bitcol bit null
go

update obj set bitcol = 0
update obj set bitcol = 1 where id <= 10
go

-- 增加 tinyint 型字段 tinycol
alter table obj add tinycol tinyint null
go

update obj set tinycol = 0
update obj set tinycol = 1 where id <= 10
go

-- 建立 bit 索引和 tinyint 索引
create index ix_obj_bitcol on obj(bitcol)
create index ix_obj_tinycol on obj(tinycol)
go

测试表建立好,首先来测试下逻辑 IO(logical reads),在实验里我没有测试 语句的执行时间,一方面是数据量较小;另一方面我认为对于本实验环境,逻辑 IO 在 很大程度上决定了执行时间。如果你觉得不放心的话,可以加大数据量,深入地 测试下。首先打开统计磁盘 IO 和查看查询计划的 set 选项:

set statistics io on
set statistics profile on

然后,执行下面的语句:

select count(*) from obj where bitcol = 1
select count(*) from obj where bitcol = 0

select count(*) from obj where tinycol = 1
select count(*) from obj where tinycol = 0

磁盘IO统计结果:

Table 'obj'. Scan count 1, logical reads 2, physical reads
Table 'obj'. Scan count 1, logical reads 5, physical reads

Table 'obj'. Scan count 1, logical reads 2, physical reads
Table 'obj'. Scan count 1, logical reads 5, physical reads

查询计划:

SELECT COUNT(*) FROM obj WHERE bitcol=@1
|--Index Seek(OBJECT:(obj.ix_obj_bitcol), SEEK:(obj.bitcol=@1) ORDERED FORWARD)

SELECT COUNT(*) FROM obj WHERE bitcol=@1
|--Index Seek(OBJECT:(obj.ix_obj_bitcol), SEEK:(obj.bitcol=@1) ORDERED FORWARD)

SELECT COUNT(*) FROM obj WHERE tinycol=@1
|--Index Seek(OBJECT:(obj.ix_obj_tinycol), SEEK:(obj.tinycol=@1) ORDERED FORWARD)

SELECT COUNT(*) FROM obj WHERE tinycol=@1
|--Index Seek(OBJECT:(obj.ix_obj_tinycol), SEEK:(obj.tinycol=@1) ORDERED FORWARD)

从执行结果中可以看到:在 SQL Server 2005 中,(1)可以使用 bit 索引; (2)bit 索引和 tinyint 索引查询数据的方式都是 index seek(索引查找); (3)使用 bit 索引和 tinyint 索引的两组查询耗费相同的逻辑IO。

二、在 SQL Server 2000 中测试 bit 索引和 tinyint 索引。

利用 DTS 从 SQL Server 2005 中导入测试表,导入数据后,需要建立主键和索引:

alter table obj add constraint pk_obj primary key (id)
go

create index ix_obj_bitcol on obj(bitcol)
create index ix_obj_tinycol on obj(tinycol)
go

打开 set 统计选项:

set statistics io on
set statistics profile on

执行查询:

select count(*) from obj where bitcol = 1
select count(*) from obj where bitcol = 0

select count(*) from obj where tinycol = 1
select count(*) from obj where tinycol = 0

磁盘IO统计结果:

Table 'obj'. Scan count 1, logical reads 4, physical reads 0, read-ahead reads 0.
Table 'obj'. Scan count 1, logical reads 4, physical reads 0, read-ahead reads 0.

Table 'obj'. Scan count 1, logical reads 2, physical reads 0, read-ahead reads 0.
Table 'obj'. Scan count 1, logical reads 4, physical reads 0, read-ahead reads 0.

查询计划:

select count(*) from obj where bitcol = 1
|--Index Scan(OBJECT:(obj.ix_obj_bitcol), WHERE:(Convert(obj.bitcol)=1))

select count(*) from obj where bitcol = 0
|--Index Scan(OBJECT:(obj.ix_obj_bitcol), WHERE:(Convert(obj.bitcol)=0))

select count(*) from obj where tinycol = 1
|--Index Seek(OBJECT:(obj.ix_obj_tinycol), SEEK:(obj.tinycol=1) ORDERED FORWARD)

select count(*) from obj where tinycol = 0
|--Index Seek(OBJECT:(obj.ix_obj_tinycol), SEEK:(obj.tinycol=0) ORDERED FORWARD)

从执行结果中可以看到:在 SQL Server 2000 中,(1)也可以使用 bit 索引; (2)bit 索引和 tinyint 索引查询数据的方式分别是 index scan(整个索引扫描)和 seek(索引查找);(3)使用 bit 索引和 tinyint 索引的两组查询耗费不同的逻辑IO,bit 索引需要扫描整个索引,而 tinyint 索引仅仅需要搜寻部分索引,因此在一定条件下,tinyint 索引显然比 bit 索引的性能要好。 (4)还可以看出,在 bit 字段在做比较运算的时候,SQL Server 2000 转换 bit数据为其他数据类型类型(很可能是 int)“WHERE:(Convert(obj.bitcol)=1)”, 因此不能 Index seek,而只能是 index scan。

为了提高 bit 索引性能,我们改进查询:

select count(*) from obj where bitcol = convert(bit,1)
select count(*) from obj where bitcol = convert(bit,0)

此时查看查询计划:

select count(*) from obj where bitcol = convert(bit, 1)
|--Index Seek(OBJECT:(obj.ix_obj_bitcol), SEEK:(obj.bitcol=1) ORDERED FORWARD)

select count(*) from obj where bitcol = convert(bit, 0)
|--Index Seek(OBJECT:(obj.ix_obj_bitcol), SEEK:(obj.bitcol=0) ORDERED FORWARD)

通过以上测试可以得出:(1)SQL Server 2000 中 和 SQL Server 2005 中都可以在 bit 字段上建立索引。 SQL Server 2000 帮助文档中指出的“不能在 bit 字段上建立索引的”说法是不正确的。 (2)查询数据时候,在 SQL Server 2000 中能利用 bit 索引的 Index Scan,性能逊色于 tinyint 索引。 (3)在 SQL Server 2000 中可以通过改进查询来使用 bit 索引上的 Index seek。 (4)在 SQL Server 2005 中可以利用 bit 索引进行 index seek,性能和 tinyint 索引基本相同。

你可能感兴趣的:(SQL Server)