建在含有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的环境下验证:
首先,建立测试环境:
让我们从两个角度验证,
第一个是执行计划角度:
运行语句:
得到结果:
执行计划:
结论:明显的,在where子句中使用is null或is not null的语句一样可以利用建在含有null值的字段上的索引。
第二个是页面数据存储角度:
首先找出索引序号:
得到结果:
找出数据页所在地:
得到:
先打开跟踪标记3604:
先对升序排列序列号为2的索引观察内部存储方式:
得到:
在消息中可以看到:
证明:“null值会被归到一块。如果是升序的话,null值被归到数据页的前端。如果有多条null值数据,则按先来后到的顺序依次排列。”
下面对降序排列序列号为3的索引观察内部存储方式:
得到:
在消息中可以看到:
证明:“null值会被归到一块。如果是降序序的话,null值被归到数据页的尾端。如果有多条null值数据,则按先来后到的顺序依次排列。”
如果你运行语句:
得到:
这也从一个侧面印证了“如果有多条null值数据,则按先来后到的顺序依次排列”的推论(因为先插入了"3, NULL",后插入"5, 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
相关文章推荐
- MySQL导出和导入含有二进制字段的数据
- SQL 通过syscolumns.xtype动态查找指定数据类型字段所包含的数据
- Oracle查询表中指定字段的数据是否含有某个字符
- 导出查询中的数据到Excel,包含字段名,文件为真正的Excel文件
- 使用mysql查询数据表中某个字段包含某个数值
- Ajax请求php返回json对象数据中包含有数字索引和字符串索引,在for in循环中取出数据的顺序问题
- 为应用程序池defaultAppPool提供服务的进程在于world wide web publishing服务通信时遇到致命错误 进程id为1356. 数据字段包含错误号
- ABAP--在查询条件只包含部分索引字段时,如何使用索引
- MySQL中大数据表增加字段,增加索引实现
- 向含有自增字段的mysql数据库插入数据的问题
- 通过游标遍历数据库中的用户表,找出所有含有NULL值字段的表名和字段名
- 利用sqlite创建一个数据user,其含有一张表person,该person表中含有三个列,其中第一字段为主键int类型的,其他两个字段自定义数据类型和名称。
- 清理8组nodes中表的历史数据,平均每个node中的表有1.5亿条记录,需要根据date_created字段清理8000W数据记录,这个字段没有索引。
- 清理8组nodes中表的历史数据,平均每个node中的表有1.5亿条记录,需要根据date_created字段清理8000W数据记录,这个字段没有索引。
- 将表中的一字段里包含“0”的数据 改成 保留10位小数为0.0027397260。反之,为自身数
- 更新SQL SERVER中所有表中包含字段为[Nd]的表值数据
- 常用查询(一):查看是否有计算列、出现过的字段类型、含有聚集索引的表
- [求教] - 关于ASP中如何对数据表字段中所包含的特殊字符 ' 与 " 进行转义处理?
- 向包含无重复值索引的access数据库中批量添加数据
- php读取含有3w条以上数据的csv文件,并选择性将相应字段的数据导入至mysql