您的位置:首页 > 数据库

建在含有null值的字段上的索引会包含null值数据

2010-01-22 13:41 218 查看
今天,一个朋友问了个问题:“如果一个表中的某个列的数据有null的,那我为这个列建索引,然后查询有用吗?”

我回答:“有用。”

他又说(可能是从网上摘抄下来的):“不能用null作索引,任何包含null值的列都将不会被包含在索引中。即使索引有多列这样的情况下,只要这些列中有一列含有null,该列就会从索引中排除。也就是说如果某列存在空值,即使对该列建索引也不会提高性能。任何在where子句中使用is null或is not null的语句优化器是不允许使用索引的。”

我的回答是:“如果null值的那一列不归到索引里,那不是数据少了。所以null值肯定也会反映在索引里。”

 

其实,null值反映在索引中的排列规则是:null值会被归到一块。而且,如果是升序的话,null值被归到数据页的前端,如果是降序的话,null值被归到数据页的尾端。如果有多条null值数据,则按先来后到的顺序依次排列。

 

下面,就让我们在SQL Server 2005的环境下验证:

 

首先,建立测试环境:

USE master
GO
IF EXISTS (SELECT * FROM sys.databases WHERE [name] = N'Test_NullIndex')
DROP DATABASE Test_NullIndex
GO
CREATE DATABASE Test_NullIndex
GO
USE Test_NullIndex
GO
CREATE TABLE dbo.TNull
(
a INT PRIMARY KEY CLUSTERED,
b NVARCHAR(50) NULL
)
GO
-- 建立两个在相同字段但排序方式不同的索引
CREATE INDEX Ix_b_Asc ON dbo.TNull(b ASC)
GO
CREATE INDEX Ix_b_Desc ON dbo.TNull(b DESC)
GO
INSERT INTO dbo.TNull (a, b) VALUES(1, N'a')
INSERT INTO dbo.TNull (a, b) VALUES(2, N'b')
INSERT INTO dbo.TNull (a, b) VALUES(3, NULL)
INSERT INTO dbo.TNull (a, b) VALUES(4, N'c')
INSERT INTO dbo.TNull (a, b) VALUES(5, NULL)
GO
  
 

让我们从两个角度验证,

   第一个是执行计划角度:

      运行语句:
SELECT * FROM dbo.TNull WITH(INDEX(Ix_b_Asc)) WHERE b IS NULL
      

      得到结果:


      执行计划:


      结论:明显的,在where子句中使用is null或is not null的语句一样可以利用建在含有null值的字段上的索引。

  第二个是页面数据存储角度:

      首先找出索引序号:
SELECT [name], index_id FROM sys.indexes WHERE object_id = object_id('TNull') AND [name] IN (N'Ix_b_Asc', N'Ix_b_Desc')
      

      得到结果:


      找出数据页所在地:
DBCC IND(Test_NullIndex, N'TNull', 2)
DBCC IND(Test_NullIndex, N'TNull', 3)


      得到:


      先打开跟踪标记3604:
DBCC TRACEON(3604)


      先对升序排列序列号为2的索引观察内部存储方式:
DBCC PAGE(Test_NullIndex, 1, 89, 1)
DBCC PAGE(Test_NullIndex, 1, 89, 3)
GO


      得到: 


      在消息中可以看到:
DATA:

Slot 0, Offset 0x7c, Length 8, DumpStyle BYTE

Record Type = INDEX_RECORD           Record Attributes =  NULL_BITMAP
Memory Dump @0x38BFC07C

00000000:   16030000 000200fd †††††††††††††††††††........

Slot 1, Offset 0x92, Length 8, DumpStyle BYTE

Record Type = INDEX_RECORD           Record Attributes =  NULL_BITMAP
Memory Dump @0x38BFC092

00000000:   16050000 000200fd †††††††††††††††††††........

Slot 2, Offset 0x60, Length 14, DumpStyle BYTE

Record Type = INDEX_RECORD           Record Attributes =  NULL_BITMAP VARIABLE_COLUMNS

Memory Dump @0x38BFC060

00000000:   36010000 000200fc 01000e00 6100††††††6...........a.

Slot 3, Offset 0x6e, Length 14, DumpStyle BYTE

Record Type = INDEX_RECORD           Record Attributes =  NULL_BITMAP VARIABLE_COLUMNS

Memory Dump @0x38BFC06E

00000000:   36020000 000200fc 01000e00 6200††††††6...........b.

Slot 4, Offset 0x84, Length 14, DumpStyle BYTE

Record Type = INDEX_RECORD           Record Attributes =  NULL_BITMAP VARIABLE_COLUMNS

Memory Dump @0x38BFC084

00000000:   36040000 000200fc 01000e00 6300††††††6...........c.
      

      证明:“null值会被归到一块。如果是升序的话,null值被归到数据页的前端。如果有多条null值数据,则按先来后到的顺序依次排列。

     

      下面对降序排列序列号为3的索引观察内部存储方式:
DBCC PAGE(Test_NullIndex, 1, 114, 1)
DBCC PAGE(Test_NullIndex, 1, 114, 3)
GO


      得到:


      在消息中可以看到:
DATA:

Slot 0, Offset 0x84, Length 14, DumpStyle BYTE

Record Type = INDEX_RECORD           Record Attributes =  NULL_BITMAP VARIABLE_COLUMNS

Memory Dump @0x3A45C084

00000000:   36040000 000200fc 01000e00 6300††††††6...........c.

Slot 1, Offset 0x6e, Length 14, DumpStyle BYTE

Record Type = INDEX_RECORD           Record Attributes =  NULL_BITMAP VARIABLE_COLUMNS

Memory Dump @0x3A45C06E

00000000:   36020000 000200fc 01000e00 6200††††††6...........b.

Slot 2, Offset 0x60, Length 14, DumpStyle BYTE

Record Type = INDEX_RECORD           Record Attributes =  NULL_BITMAP VARIABLE_COLUMNS

Memory Dump @0x3A45C060

00000000:   36010000 000200fc 01000e00 6100††††††6...........a.

Slot 3, Offset 0x7c, Length 8, DumpStyle BYTE

Record Type = INDEX_RECORD           Record Attributes =  NULL_BITMAP
Memory Dump @0x3A45C07C

00000000:   16030000 000200fd †††††††††††††††††††........

Slot 4, Offset 0x92,
4000
Length 8, DumpStyle BYTE

Record Type = INDEX_RECORD           Record Attributes =  NULL_BITMAP
Memory Dump @0x3A45C092

00000000:   16050000 000200fd †††††††††††††††††††........


      证明:“null值会被归到一块。如果是降序序的话,null值被归到数据页的尾端。如果有多条null值数据,则按先来后到的顺序依次排列。”

 

如果你运行语句:
SELECT TOP 1 * FROM dbo.TNull WITH(INDEX(Ix_b_Asc)) WHERE b IS NULL
GO
SELECT TOP 1 * FROM dbo.TNull WITH(INDEX(Ix_b_Desc)) WHERE b IS NULL
GO


得到:


这也从一个侧面印证了“如果有多条null值数据,则按先来后到的顺序依次排列”的推论(因为先插入了"3, NULL",后插入"5, NULL")。

 

最后,清理测试环境:
DROP DATABASE Test_NullIndex
GO
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
相关文章推荐