关于聚集索引与非聚集索引的讨论:
A、区别:
聚集索引一个表只能有一个,而非聚集索引一个表可以存在多个
聚集索引存储记录是物理上连续存在,而非聚集索引是逻辑上的连续,物理存储并不连续。
B、关于索引的几个问题:
DEMO 分析:
一个学生表student,里面是学生号id,学生姓名,学生所在城市ID,学生成绩(总分)。
· 问:如果想按姓名查询,如何做优化?
· 答:在姓名字段上建立索引。
· 问:建立什么类型的索引?
· 答:建立非聚集索引。
· 问:为什么?
· 答:一般有范围查询的需求,可以考虑在此字段上创建聚集索引。
· 问:学分有重复性,在学分字段上创建聚集索引能行吗? ....沉思,不能创建吗?之前的项目好像真这样做过
· 答:应该可以吧。
· 问:聚集索引的约束是什么?
· 答:唯一性啊?
· 问:既然是唯一性,那么学分字段上还能创建聚集索引吗?....再次沉思,应该可以啊,但索引的约束又怎么说呢?
· 答:应该可以的,以前用过。
○1 :聚集索引的约束是唯一性,是否要求字段也是唯一的呢?
分析:如果认为是的朋友,可能是受系统默认设置的影响,一般我们指定一个表的主键,如果这个表之前没有聚集索引,同时建立主键时候没有强制指定使用非聚集索引,SQL 会默认在此字段上创建一个聚集索引,而主键都是唯一的,所以理所当然的认为创建聚集索引的字段也需要唯一,因此创建了聚集索引的列,并不一定要求其列字段值的唯一性。结论:聚集索引可以创建在任何一列你想创建的字段上,这是从理论上讲,实际情况并不能随便指定,否则在性能上会是恶梦。
○2:为什么聚集索引可以创建在任何一列上,如果此表没有主键约束,即有可能存在重复行数据呢?
粗一看,这还真是和聚集索引的约束相背,但实际情况真可以创建聚集索引,分析其原因是:如果未使用 UNIQUE 属性创建聚集索引,数据库引擎将向表自动添加一个四字节uniqueifier列。必要时,数据库引擎 将向行自动添加一个 uniqueifier 值,使每个键唯一。此列和列值供内部使用,用户不能查看或访问。
○3 :是不是聚集索引就一定要比非聚集索引性能优呢?
如果想查询学分在60-90 之间的学生的学分以及姓名,在学分上创建聚集索引是否是最优的呢?
答:否。既然只输出两列,我们可以在学分以及学生姓名上创建联合非聚集索引,此时的索引就形成了覆盖索引,即索引所存储的内容就是最终输出的数据,这种索引在比以学分为聚集索引做查询性能更好。
○4 :在数据库中通过什么描述聚集索引与非聚集索引的?
索引是通过二叉树的形式进行描述的,我们可以这样区分聚集与非聚集索引的区别:聚集索引的叶节点就是最终的数据节点,而非聚集索引的叶节仍然是索引节点,但它有一个指向最终数据的指针。
○5:在主键是创建聚集索引的表在数据插入上为什么比主键上创建非聚集索引表速度要慢?
有了上面第四点的认识,我们分析这个问题就有把握了,在有主键的表中插入数据行,由于有主键唯一性的约束,所以需要保证插入的数据没有重复。我们来比较下主键为聚集索引和非聚集索引的查找情况:聚集索引由于索引叶节点就是数据页,所以如果想检查主键的唯一性,需要遍历所有数据节点才行,但非聚集索引不同,由于非聚集索引上已经包含了主键值,所以查找主键唯一性,只需要遍历所有的索引页就行,这比遍历所有数据行减少了不少IO 消耗。这就是为什么主键上创建非聚集索引比主键上创建聚集索引在插入数据时要快的真正原因。
DEMO:有索引与无索引查询效率分析
A、堆表扫描:
create table tb_heap (co1 int identity (1,1),co2 char(10))
go
insert into tb_heap (co2)
values (‘demo‘)
go 1000
select * from tb_heap
显示预估的执行计划:
查询 → 显示预估的执行计划
显示占有的磁盘空间:
sp_spaceused tb_heap
B、聚集索引扫描:
create table tb_index (co1 int identity (1,1) primary key,co2 char(10))
go
insert into tb_index (co2)
values (‘demo‘)
go 1000
当在表中创建了主键约束后,将自动将主键创键为聚集索引,当全表查询时,查询方式为聚集索引扫描.
select * from tb_index
显示预估的执行计划:
显示占有的磁盘空间:
sp_spaceused tb_index
当在表中创建了主键约束后,将自动将主键创键为聚集索引,当带where语句执行条件查询时,查询方式为聚集索引查找.
select * from tb_index
where co1 = 10
显示预估的执行计划:
管理索引
根据数据库的功能,可以在数据库设计器中创建三种索引:唯一索引、主键索引和聚集索引。
唯一索引: 唯一索引是不允许其中任何两行具有相同索引值的索引。
当现有数据中存在重复的键值时,大多数数据库不允许将新创建的唯一索引与表一起保存。数据库还可能防止添加将在表中创建重复键值的新数据。例如,如果在employee 表中职员的姓(lname)上创建了唯一索引,则任何两个员工都不能同姓。
主键索引:
数据库表经常有一列或列组合,其值唯一标识表中的每一行。该列称为表的主键。在数据库关系图中为表定义主键将自动创建主键索引,主键索引是唯一索引的特定类型。该索引要求主键中的每个值都唯一。当在查询中使用主键索引时,它还允许对数据的快速访问。
聚集索引
在聚集索引中,表中行的物理顺序与键值的逻辑(索引)顺序相同。一个表只能包含一个聚集索引。如果某索引不是聚集索引,则表中行的物理顺序与键值的逻辑顺序不匹配。与非聚集索引相比,聚集索引通常提供更快的数据访问速度。
语法:
CREATE [UNIQUE] [CLUSTERED | NONCLUSTERED]
INDEX index_name ON {table | view } column [ ASC | DESC ][,...n])
[WITH [PAD_INDEX] [ [, ]FILLF ACTOR = fillfactor][ [, ] IGNORE_DUP_KEY]
[ [, ] DROP_EXISTING] [ [, ] STATISTICS_NORECOMPUTE]
[ [, ] SORT_IN_TEMPDB ]]
UNIQUE:表示唯一索引,可选。
CLUSTERED、NONCLUSTERED:表示聚集索引还是非聚集索引,可选。
FILLFACTOR:表示填充因子,指定一个0 到100 之间的值,该值指示索引页填满的空间所占的百分比。
注:数据类型为TEXT、NTEXT、IMAGE 或BIT 的列不能作为索引的列。 由于索引的宽度不能超过900 个字节,因此数据类型为CHAR、VARCHAR、BINARY 和VARBINARY的列的列宽度超过了900 字节,或数据类型为NCHAR、NVARCHAR 的列的列宽度超过了450 个字节时也不能作为索引的列。
创建主键索引:
创建tb_ha1 和tb_ha2 2 张表,其中tb_ha2 配置主键使用统计开关显示查询时间
--开启统计开关
set statistics profile off
set statistics io off
set statistics time off
--开启统计开关
set statistics profile on
set statistics io on
set statistics time on
创建一张普通表 tb_ha1
create table tb_ha1 (docno nvarchar(20), part nvarchar(30))
go
insert into tb_ha1
select LEFT (convert (nvarchar(128),newid()),20),left(convert
(nvarchar(128),newid()),30)
go 5000
select * from tb_ha1
开启统计开关,查看查询时间:
SQL Server 分析和编译时间:
CPU 时间= 0 毫秒,占用时间= 1 毫秒。
(5000 行受影响)
表‘tb_ha1‘。扫描计数1,逻辑读取75 次,物理读取0 次,预读0 次,lob 逻辑读取0 次,lob 物理读取0 次,lob 预读0 次。
(2 行受影响)
SQL Server 执行时间:
CPU 时间= 31 毫秒,占用时间= 173 毫秒。
创建表tb_ha2,并定义主键索引:
create table tb_ha2 (docno nvarchar(20) primary key, part nvarchar(30))
go
insert into tb_ha2
select LEFT (convert (nvarchar(128),newid()),20),left(convert
(nvarchar(128),newid()),30)
go 5000
开启统计开关,查看查询时间:
Select * from tb_ha2
SQL Server 分析和编译时间:
CPU 时间= 0 毫秒,占用时间= 1 毫秒。
(5000 行受影响)
表‘tb_ha2‘。扫描计数1,逻辑读取108 次,物理读取0 次,预读0 次,lob 逻辑读取0 次,lob 物
理读取0 次,lob 预读0 次。
(2 行受影响)
SQL Server 执行时间:
CPU 时间= 0 毫秒,占用时间= 132 毫秒。
LEFT (convert (nvarchar(128),newid()),20):
使用newid(一个随机数),转换数据类型为nvarchar(128),从左开始取值20 位.
创建唯一聚集索引:
DEMO:为tb_ha1 创建唯一聚集索引:
create unique CLUSTERED
index index_docno
on tb_ha1 (docno)
select * from tb_ha1
where docno = ‘0046CC15-B219-4745-B‘
检查表tb_ha1是否创建了索引:
sp_helpindex tb_ha1
创建一个非聚集索引:
DEMO:
create table tb_ha3 (docno nvarchar(20), part nvarchar(30))
go
insert into tb_ha3
select LEFT (convert (nvarchar(128),newid()),20),left(convert
(nvarchar(128),newid()),30)
go 5000
创建索引:
create nonclustered
index index_part
on tb_ha3 (part)
查看所创建的索引:
sp_helpindex tb_ha3
创建复合索引:
为表tb2 创建一个复合索引:
sp_help tb2
select * from tb2
为列ctoyid 及cBrandld 创建一个复合索引:
create
index index_cBr
on tb2 (ctoyid,cBrandld)
查看索引:
sp_helpindex tb2
使用图形化界面创建索引:
数据库 → 表 → 设计 → 列 → 右键 → 索引/键
索引重命名:
使用sp_rename:
语法 :
sp_rename
[ @objname = ] ‘object_name‘ ,
[ @newname = ] ‘new_name‘
[ , [ @objtype = ] ‘object_type‘ ]
删除索引:
用DROP INDEX命令删除索引
DROP INDEX 命令可以删除一个或多个当前数据库中的索引
语法:
DROP INDEX ‘tablename.indexname‘ [,...n]
DROP INDEX 命令不能删除由CREATETABLE 或ALTER TABLE 命令创建的PRIMARY KEY 或UNIQUE 约束索引。也不能删除系统表中的索引
DEMO:
drop index tb2.idx_ctoy
T-SQL 统一命名:
-- pk: 主键
-- fk:外键
-- idx: 索引
-- check:check 约束