# 聚簇、非聚簇索引结构及B树、位图、散列索引

(Clustered Index)

(Non- Clustered Index)

 动作 使用聚簇索引 使用非聚簇索引 列经常被分组排序 应 应 返回某范围内的数据 应 不应 一个或极少不同值 不应 不应 小数目的不同值 应 不应 大数目的不同值 不应 应 频繁更新的列 不应 应 外键列 应 应 主键列 应 应 频繁修改索引列 不应 应

SQL
Server

1. CREATE TABLE [dbo].[Table1](
2.   [ID] [int] IDENTITY(1,1) NOT NULL,
3.   [Data1] [int] NOT NULL DEFAULT ((0)),
4.   [Data2] [int] NOT NULL DEFAULT ((0)),
5.   [Data3] [int] NOT NULL DEFAULT ((0)),
6.   [Name1] [nvarchar](50) NOT NULL DEFAULT (”),
7.   [Name2] [nvarchar](50) NOT NULL DEFAULT (”),
8.   [Name3] [nvarchar](50) DEFAULT (”),
9.   [DTAt] [datetime] NOT NULL DEFAULT (getdate())

1. declare @i int
2. set @i = 1
3. while @i < 100000
4. begin
5. insert into Table1 ([Data1] ,[Data2] ,[Data3] ,[Name1],[Name2] ,[Name3])
6. values(@i, 2* @i,3*@i, CAST(@i AS NVARCHAR(50)), CAST(2*@i AS NVARCHAR(50)), CAST(3*@i AS NVARCHAR(50)))
7. set @i = @i + 1
8. end
9. update table1 set dtat= DateAdd (s, data1, dtat)

1. SET STATISTICS IO ON;
2. SET STATISTICS TIME ON;

1. SELECT * FROM Table1 WHERE Data1 = 2 ORDER BY DTAt DESC;

1. Table ‘Table1’. Scan count 1, logical reads 911, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.
2. SQL Server Execution Times:
3. CPU time = 16 ms, elapsed time = 7 ms.

1. CREATE NONCLUSTERED INDEX [N_Data1] ON [dbo].[Table1]
2. (
3. [Data1] ASC
4. )WITH (SORT_IN_TEMPDB = OFF, DROP_EXISTING = OFF, IGNORE_DUP_KEY = OFF, ONLINE = OFF) ON [PRIMARY]
5. CREATE NONCLUSTERED INDEX [N_DTat] ON [dbo].[Table1]
6. (
7. [DTAt] ASC
8. )WITH (SORT_IN_TEMPDB = OFF, DROP_EXISTING = OFF, IGNORE_DUP_KEY = OFF, ONLINE = OFF) ON [PRIMARY]

1. Table ‘Table1’. Scan count 1, logical reads 5, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.
2. SQL Server Execution Times:
3. CPU time = 0 ms, elapsed time = 39 ms.

1. CREATE CLUSTERED INDEX [C_Data1_DTat] ON [dbo].[Table1]
2. (
3. [Data1] ASC,
4. [DTAt] ASC
5. )WITH (SORT_IN_TEMPDB = OFF, DROP_EXISTING = OFF, IGNORE_DUP_KEY = OFF, ONLINE = OFF) ON [PRIMARY]

1. Table ‘Table1’. Scan count 1, logical reads 3, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.
2. SQL Server Execution Times:
3. CPU time = 0 ms, elapsed time = 1 ms.

1. Table ‘Table1’. Scan count 1, logical reads 238, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.
2. SQL Server Execution Times:
3. CPU time = 16 ms, elapsed time = 22 ms.

1. Table ‘Table1’. Scan count 1, logical reads 3, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.
2. SQL Server Execution Times:
3. CPU time = 0 ms, elapsed time = 1 ms.

Index seek 为什么比 Index scan好？

Index scan多半是出现在索引列在表达式中。数据库

1、缺省情况下建立的索引是非聚簇索引，但有时它并不是最佳的。在非群集索引下，数据在物理上随机存放在数据页上。合理的索引

a.有大量重复值、且经常有范围查询（ > ,< ，> =,< =）和order by、group by发生的列，可考

b.经常同时存取多列，且每列都含有重复值可考虑建立组合索引；

c.组合索引要尽量使关键查询形成索引覆盖，其前导列一定是使用最频繁的列。索引虽有助于提高性能但不是索引越多越好，恰好相反过多的索引会导致系统低效。

2、ORDER BY和GROPU BY使用ORDER BY和GROUP BY短语，任何一种索引都有助于SELECT的性能提高。

3、多表操作在被实际执行前，查询优化器会根据连接条件，列出几组可能的连接

4、任何对列的操作都将导致表扫描，它包括数据库

、计算表达式等等，查询时要尽可能将操作移至等号右边。

5、IN、OR子句常会使用工作表，使索引失效。如果不产生大量重复值，可以考虑把子句拆开。拆开的子句中应该包含索引。

Sql的优化原则2：

1、只要能满足你的需求，应尽可能使用更小的数据类型：例如使用MEDIUMINT代替INT

2、尽量把所有的列设置为NOT NULL，如果你要保存NULL，手动去设置它，而不是把它设为默认值。

3、尽量少用VARCHAR、TEXT、BLOB类型

4、如果你的数据只有你所知的少量的几个。最好使用ENUM类型

1. use master
2. select * from profiletrace order by duration desc;

B树索引

1.索引不存储null值。

更准确的说，单列索引不存储null值，复合索引不存储全为ｎｕｌｌ的值

索引不能存储Null，所以对这列采用is null条件时，因为索引上根本没Null值，不能利用到索引，只

为什么索引列不能存Null值呢？将索引列值进行建树，其中必然涉及到诸多的比较操作。Null值

B树索引测试1：NULL是否存在索引上。

create table btree_test(id number,code varchar2(10));

create index idx_btree_test_id on btree_test(id,code);

select object_id from user_objects where object_name=’IDX_BTREE_TEST_ID’;

alter session set events ‘immediate trace name treedump level 59097’;

insert into btree_test values(null,null);

alter session set events ‘immediate trace name treedump level 59097’;

insert into btree_test values(null,’1′);

alter session set events ‘immediate trace name treedump level 59097’;

insert into btree_test values(1,null);

alter session set events ‘immediate trace name treedump level 59097’;

*** 2013-07-19 14:56:41.827

—– begin tree dump

leaf: 0x140142c 20976684 (0: nrow: 0 rrow: 0)

—– end tree dump

*** 2013-07-19 14:56:54.480

—– begin tree dump

leaf: 0x140142c 20976684 (0: nrow: 1 rrow: 1)

—– end tree dump

*** 2013-07-19 14:57:08.139

—– begin tree dump

leaf: 0x140142c 20976684 (0: nrow: 2 rrow: 2)

—– end tree dump

nrow当前节点所含索引条目的数量（包括delete的条目）

rrow有效的索引条目的数量

也可以这样看：

SELECT num_rows  FROM user_indexes t   WHERE t.index_name =’btree_test’;

2.不适合键值较少的列（重复数据较多的列）。

假如索引列TYPE有5个键值，如果有1万条数据，那么 WHERE TYPE = 1将访问表中的2000个数据块。

如果全表扫描，假设10条数据一个数据块，那么只需访问1000个数据块，既然全表扫描访问的数据块

3.前导模糊查询不能利用索引(like ‘%XX’或者like ‘%XX%’)

假如有这样一列code的值为’AAA’,’AAB’,’BAA’,’BAB’ ,如果where code like ‘%AB’条件，由于前面是

就是用位图表示的索引，对列的每个键值建立一个位图。

如test表中有state这样一列，数据如下：

10    20    30    20    10    30    10    30    20    30

BLOCK1    KEY=10  1    0    0    0    1    0    1    0    0    0

BLOCK2    KEY=20  1    0    0    0    1    0    1    0    0    0

BLOCK3    KEY=30  1    0    0    0    1    0    1    0    0    0

1.相对于B*Tree索引,占用的空间非常小,创建和使用非常快。

位图索引由于只存储键值的起止Rowid和位图,占用的空间非常少。

2.不适合键值较多的列。

3.不适合update、insert、delete频繁的列。

4.可以存储null值。

B*Tree索引由于不记录空值,当基于is null的查询时,会使用全表扫描,而对位图索引列进

5.当select count(XX) 时,可以直接访问索引中一个位图就快速得出统计数据。

6.当根据键值做and,or或 in(x,y,..)查询时,直接用索引的位图进行或运算,快速得出结果行数

1.只适合等值查询（包括= <> 和in），不适合模糊或范围查询

原文作者：B树
原文地址: https://blog.csdn.net/shen_gang/article/details/18924519
本文转自网络文章，转载此文章仅为分享知识，如有侵权，请联系博主进行删除。