SQL server速成之路索引与数据完整性
Posted 这个昵称我想了20分钟
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了SQL server速成之路索引与数据完整性相关的知识,希望对你有一定的参考价值。
🎉个人主页:这个昵称我想了20分钟
✨往期专栏:【速成之路】jQuery
🎖️专栏:【速成之路】SQL server
🔓往期回顾:
【SQL server速成之路】数据库基础
【SQL server速成之路】数据库和表(一)
【SQL server速成之路】数据库和表(二)
【SQL server速成之路】数据库的查询
【SQL server速成之路】数据库的视图和游标
【SQL server速成之路】T-SQL语言(一)
【SQL server速成之路】T-SQL语言(二)
【SQL server速成之路】函数
索引与数据完整性
索引
在数据库系统中建立索引主要有以下作用:
- 快速存取数据;
- 保证数据记录的唯一性;
- 实现表与表之间的参照完整性;
- 在使用ORDER BY、GROUP BY子句进行数据检索时,利用索引可以减少排序和分组的时间。
注:
- Sql server支持在表中任何列(包括计算列)上定义索引;
- 唯一索引:不会有两行记录的索引键值相同;
- 不唯一索引:多个行共享同一索引键值;
- 复合索引:多列组合创建的索引。
一.索引的分类
1. 聚集索引
聚集索引将数据行的键值在表内排序并存储对应的数据记录,使得数据表物理顺序与索引顺序一致。SQL Server 2012是按B树(BTREE)方式组织聚集索引的,B树方式构建为包含了多个节点的一棵树。顶部的节点构成了索引的开始点,叫做根。每个节点中含有索引列的几个值,一个节点中的每个值又都指向另一个节点或者指向表中的一行,一个节点中的值必须是有序排列的。指向一行的一个节点叫做叶子页。叶子页本身也是相互连接的,一个叶子页有一个指针指向下一组。
注:表中的顺序和索引顺序一致。
2. 非聚集索引
非聚集索引完全独立于数据行的结构。SQL Server 2012也是按B树组织非聚集索引的,与聚集索引不同之处在于:非聚集索引B树的叶节点不存放数据页信息,而是存放非聚集索引的键值,并且每个键值项都有指针指向包含该键值的数据行。
对于非聚集索引,表中的数据行不按非聚集键的次序存储。
在非聚集索引内,从索引行指向数据行的指针称为行定位器。行定位器的结构取决于数据页的存储方式是堆集还是聚集。对于堆集,行定位器是指向行的指针。对于有聚集索引的表,行定位器是聚集索引键,只有在表上创建聚集索引时,表内的行才按特定顺序存储。这些行按聚集索引键顺序存储。如果一个表只有非聚集索引,它的数据行将按无序的堆集方式存储。
注:
- 一个表中最多只能有一个聚集索引,可以有多个非聚集索引;
- 创建索引时先创建聚集索引,然后再创建非聚集索引。
二. 系统表sysindexes
系统表sysindexes的主要字段如下表所示。
用户创建数据库时,系统自动创建系统表,用户创建的每个索引均将在系统表中登记。
三、索引的创建
在xsbook数据库中,经常要对xs、book和jy这3个表查询和更新,为了提高查询和更新速度,可以考虑对3个表建立如下索引:
(1)对于xs表,按借书证号建立主键索引(PRIMARY KEY约束),索引组织方式为聚集索引;按姓名建立非唯一索引,索引组织方式为非聚集索引;
(2)对于book表,按ISBN建立主键索引或者唯一索引,索引组织方式为聚集索引;
(3)对于jy表,按借书证号+ISBN建立唯一索引,索引组织方式为聚集索引。
1.界面方式创建索引
在“对象资源管理器”中展开“数据库xsbook”→展开“表”中的“dbo.xs”→右击其中的“索引”项,在弹出的快捷菜单上选择“新建索引”菜单项的“非聚集索引”。这时,用户可以在弹出的“新建索引”窗口中输入索引名称(索引名在表中必须唯一),如ck_xs,(如果是唯一索引,需要勾选“唯一”复选框)。单击新建索引窗口的“添加”按钮→在弹出选择列窗口(如图所示)中选择要添加的列→添加完毕后,单击“确定”按钮。
除了使用上面的方法创建索引之外,还可以直接在表设计器窗口创建索引。在表设计器窗口创建索引的方法如下:
(1)右击表名,在弹出的快捷菜单中选择“设计”菜单项。
(2)在“表设计器”窗口中,选择需要创建索引的属性列,右击鼠标,在弹出的快捷菜单中选择“索引/键”菜单项。
2.使用SQL命令创建索引
使用CREATE INDEX语句可以为表创建索引。语法格式:
CREATE [ UNIQUE ] /*指定索引是否唯一*/
[ CLUSTERED | NONCLUSTERED ] /*索引的组织方式*/
INDEX <索引名> /*索引名称*/
ON [ <数据库名>. [ <架构名> ] . | <架构名>. ] <表名或视图名>
( <列名> [ ASC | DESC ] [ ,...n ] ) /*索引定义的依据*/
[ WITH ( <relational_index_option> [ ,...n ] ) ] /*索引选项*/
[ ON <分区方案名> (<列名>) /*指定分区方案*/
| <文件组名> /*指定索引文件所在的文件组*/
]
[ ; ]
其中:
<relational_index_option> ::=
PAD_INDEX = ON | OFF
| FILLFACTOR = fillfactor
| SORT_IN_TEMPDB = ON | OFF
| IGNORE_DUP_KEY = ON | OFF
| STATISTICS_NORECOMPUTE = ON | OFF
| DROP_EXISTING = ON | OFF
| ONLINE = ON | OFF
| ALLOW_ROW_LOCKS = ON | OFF
| ALLOW_PAGE_LOCKS = ON | OFF
| MAXDOP = max_degree_of_parallelism
说明:
(1)UNIQUE:表示为表或视图创建唯一索引(即不允许存在索引值相同的两行)。
(2)CLUSTERED | NONCLUSTERED:用于指定创建聚集索引还是非聚集索引,前者表示创建聚集索引,后者表示创建非聚集索引。
(3)<列名>:用于指定建立索引的字段,参数n表示可以为索引指定多个字段。
(4)WITH子句:用于指定定义的索引选项。
【例1】 对于jy表,按 借书证号+ISBN 创建索引(组合索引)。
/*创建简单索引*/
IF EXISTS (SELECT name FROM sysindexes WHERE name = 'jy_num_ind ')
DROP INDEX jy.jy_num_ind
GO
CREATE INDEX jy_num_ind
ON jy (借书证号,ISBN)
【例2】 根据book表的ISBN列创建唯一聚集索引,因为指定了 CLUSTERED 子句,所以该索引将对磁盘上的数据进行物理排序。
/*创建唯一聚集索引*/
CREATE UNIQUE CLUSTERED INDEX book_id_ind
ON book(ISBN)
注:在创建索引之前,如果已经创建主键,系统会自动将主键列为聚集索引,如果未删除主键,则无法再创建新的聚集索引。
【例3】 根据xs表中借书证号字段创建唯一聚集索引。如果输入了重复键值,将忽略该INSERT或UPDATE语句。
CREATE UNIQUE CLUSTERED INDEX xs_ind
ON xs(借书证号)
WITH IGNORE_DUP_KEY
【例4】 创建一个视图,并为该视图创建索引。
/*定义视图,如下例子中,由于使用了WITH SCHEMABINDING子句,因此,定义视图时,SELECT子句中表名必须为:架构名名.视图名的形式。*/
CREATE VIEW VIEW1 WITH SCHEMABINDING
AS
SELECT 索书号,书名,姓名
FROM dbo.jy, dbo.book, dbo.xs
WHERE jy.ISBN = book.ISBN AND xs.借书证号 = jy.借书证号
/*在视图VIEW1上定义索引*/
CREATE UNIQUE CLUSTERED INDEX Ind1
ON dbo.VIEW1(索书号 ASC)
注:
- 只有在视图上定义了WITH SCHEMABINDING ,才可以创建索引;
- 为视图创建的聚集索引必须是unique索引。
四、索引的删除
1.通过界面方式删除索引
通过界面方式删除索引的主要步骤如下:启动“SQL Server Management Studio”→在“对象资源管理器”中展开数据库“xsbook”→“表”→“dbo.xs”→“索引”,选择其中要删除的索引,单击鼠标右键,在弹出的快捷菜单上选择“删除”菜单项。在打开的“删除对象”窗口单击“确定”按钮即可。
2.通过SQL命令删除索引
语法格式:
DROP INDEX
<索引名> ON <表名或视图名> [ ,...n ]
| table_or_view_name.index_name [ ,...n ]
注:DROP INDEX语句可以一次删除一个或多个索引;
不适合删除通过定义primary key或unique约束创建的索引;删除通过定义primary key或unique约束创建的索引须通过删除约束实现;
系统表上的索引不能使用drop语句。
数据完整性
一、数据完整性的分类
1.域完整性
域完整性又称为列完整性,指列数据输入的有效性。实现域完整性的方法有:限制类型(通过数据类型)、格式(通过CHECK约束和规则)或可能的取值范围(通过CHECK约束、DEFALUT定义、NOT NULL定义)等。
【例5】对于数据库xsbook的xs表,如果允许读者当前的在借图书量最多为20本,为了对读者当前的在借图书量进行限制,可以在定义xs表时,规定:“0≤借书量≤20”的约束条件达到目的。定义表xs的同时定义借书量字段的约束条件:
USE xsbook
GO
CREATE TABLE xs
(
借书证号 char(8) NOT NULL PRIMARY KEY,
姓名 char(8) NOT NULL,
性别 bit NOT NULL DEFAULT 1,
出生时间 date NOT NULL ,
/*如下语句定义字段的同时定义约束条件*/
专业 char(12) NOT NULL,
借书量 int CHECK (借书量 >=0 AND 借书量<=20) NOT NULL,
照片 varbinary(MAX) NULL
)
2.实体完整性
实体完整性又称为行的完整性,要求表中有一个主键,其值不能为空且能唯一地标识对应的记录。通过索引、UNIQUE 约束、PRIMARY KEY 约束或 IDENTITY 属性等可实现数据的实体完整性。
例如,对于xsbook数据库中xs表,借书证号作为主键,每一个读者的借书证号能唯一的标识该读者对应的行记录信息,那么在输入数据时,则不能有相同借书证号的行记录,通过对借书证号这一字段建立主键约束可实现表xs的实体完整性。
3.参照完整性
参照完整性又称引用完整性。保证主表数据与从表数据的一致性。SQL Server 2012中,参照完整性的实现是通过定义外键(外码)与主键(主码)之间或外键与唯一键之间的对应关系实现的。
码:即前面所说的关键字,又称为“键”,是能唯一标识表中记录的字段或字段组合。如果一个表有多个码,可选其中一个作为主码(主键),其余的称为后选码。
外键:如果一个表中的一个字段或若干个字段的组合是另一个表的键则称该字段或字段组合为该表的外键。xs和jy表的对应关系如图所示。
二、域完整性的实现
Sql server通过数据类型、check约束、default定义、not null定义实现域完整性。
CHECK约束实际上是字段输入内容的验证规则,表示一个字段的输入内容必须满足CHECK约束的条件,若不满足,则数据无法正常输入。对于timestamp和identity两种类型字段不能定义CHECK约束。
(1)通过界面方式创建与删除CHECK约束。
对于xsbook数据库的xs表,要求读者的借书证号必须由6个数字字符构成,并且不能为“000000”。
① 启动“SQL Server Management Studio”→在“对象资源管理器”中展开“数据库”→“xsbook”→“表”→选择“dbo.xs”,右击鼠标选择“设计”菜单项。
② 在打开的“表设计器”窗口中选择“借书证号”属性列,右击鼠标选择“CHECK约束”菜单项。
③ 在打开的 “CHECK约束”窗口(如图所示)中,单击“添加”按钮,添加一个“CHECK约束”。
(2)使用SQL语句在创建表时创建CHECK约束
利用T-SQL命令可以使用两种方式定义约束:作为列的约束或作为表的约束。
语法格式:
CREATE TABLE table_name /*指定表名*/
( <列名> <数据类型>
NOT NULL | NULL /*指定为空性*/
| [ DEFAULT <默认值表达式> ] /*指定默认值*/
| [ CONSTRAINT <约束名> ] CHECK ( logical_expression )] /*CHECK约束表达式*/
[,…n]
[ CONSTRAINT constraint_name ] CHECK ( logical_expression )][,…n]
)
(3)使用SQL语句在修改表时创建CHECK约束
语法格式:
ALTER TABLE <表名>
ADD [<列的定义>]
[CONSTRAINT <约束名>] CHECK (logical_expression)
(4)使用SQL语句删除CHECK约束
CHECK约束的删除可在对象资源管理器中通过界面删除,读者可以自己试一试,在此介绍如何利用SQL命令删除。
使用ALTER TABLE语句的DROP子句可以删除CHECK约束。语法格式:
ALTER TABLE <表名>
DROP CONSTRAINT <约束名>
三、实体完整性的实现
表中应有一个列或列的组合,其值能唯一标识表中每一行,选择这样的列或列的组合可以作为主键实现实体完整性。通过定义PRIMARY KEY约束创建主键。
PRIMARY KEY约束与UNIQUE约束的主要区别如下:
- 一个数据表只能创建一个PRIMARY KEY约束,但一个表中可根据需要对不同的列创建若干个UNIQUE约束;
- PRIMARY KEY字段的值不允许为NULL,而UNIQUE字段的值可取NULL;
- 一般创建PRIMARY KEY约束时,系统会自动产生索引,索引的默认类型为簇索引。创建UNIQUE约束时,系统会自动产生一个UNIQUE索引,索引的默认类型为非簇索引。
1.使用界面方式创建和删除PRIMARY KEY约束
(1)创建PRIMARY KEY约束
当创建主键时,系统将自动创建一个名称以“PK_”为前缀、后跟表名的主键索引,系统自动按聚集索引方式组织主键索引。
(2)删除PRIMARY KEY约束
如果要删除对表xs中对借书证号字段建立的PRIMARY KEY约束,按如下步骤进行:在“对象资源管理器”中选择dbo.xs表图标,右击鼠标,在弹出的快捷菜单中选择“设计”菜单项,进入“表设计器”窗口。选中“xs表设计器”窗口中主键所对应的行,右击鼠标,在弹出的快捷菜单中选择“删除主键”菜单项即可。
2.使用界面方式创建和删除UNIQUE约束
(1)创建UNIQUE约束
如果要对xs表中的“姓名”列创建UNIQUE约束,以保证该列取值的唯一性,可按以下步骤进行:
进入xs表的“表设计器”窗口,选择“姓名”属性列并右击鼠标,在弹出的快捷菜单中选择“索引/键”菜单项,打开“索引/键”窗口。
在窗口中单击“添加”按钮,并在右边的“标识”属性区域的“名称”一栏中输入唯一键的名称(用系统默认的名或重新取名)。在常规属性区域的“类型”一栏中选择类型为“唯一键”。
(2)删除UNIQUE约束
打开“姓名”属性列的“索引/键”窗口,选择要删除的UNIQUE约束,单击左下方的“删除”按钮,单击“关闭”按钮,保存表的修改即可。
3.使用SQL命令创建及删除PRIMARY KEY约束或UNIQUE约束
(1)创建表的同时创建PRIMARY KEY约束或UNIQUE约束
语法格式:
CREATE TABLE <表名> /*指定表名*/
( <列名> <数据类型> /*定义字段*/
[ CONSTRAINT <约束名> ] /*约束名*/
PRIMARY KEY | UNIQUE /*定义约束类型*/
[ CLUSTERED | NONCLUSTERED ] /*定义约束的索引类型*/
[, …n]
)
(2)修改表时创建PRIMARY KEY约束或UNIQUE约束
创建PRIMARY KEY约束
ALTER TABLE <表名>
ADD [ CONSTRAINT <约束名> ] PRIMARY KEY | UNIQUE
[ CLUSTERED | NONCLUSTERED]
( column [ ,...n ] )
(3)删除PRIMARY KEY约束或UNIQUE约束
删除PRIMARY KEY约束或UNIQUE约束需要使用ALTER TABLE的DROP子句。语法格式:
ALTER TABLE <表名>
DROP CONSTRAINT <约束名> [,…n]
四、参照完整性的实现
对两个相互关联的表(主表与从表)进行数据插入和删除时,通过参照完整性保证它们之间的数据一致性。
利用foreign key约束定义从表外键,利用primary key约束或unique约束定义主表中的主键或唯一键(不允许为空),可实现主表与从表之间的参照完整性。
定义表间参照关系时,先定义主键(或唯一键),再对从表定义外键。
1.使用界面方式定义表间的参照关系
例如:在数据库xsbook中要建立xs表与jy表之间的参照完整性,操作步骤如下:
(1)按照前面所介绍的方法定义主表的主键。由于之前在创建表的时候已经定义xs表中的借书证号字段为主键,所以这里就不需要再定义主表的主键了。
(2)在“对象资源管理器”中展开“数据库”→“xsbook”→选择“数据库关系图”,右击鼠标,在出现的快捷菜单中选择“新建数据库关系图”菜单项,打开“添加表”窗口。
(3)在出现的“添加表”窗口中选择要添加的表,这里选择表xs和表jy。单击“添加”按钮完成表的添加,之后单击“关闭”按钮退出窗口。
(4)在“数据库关系图设计”窗口将鼠标指向主表的主键,并拖动到从表,即将xs表中的“借书证号”字段拖动到从表jy中的“借书证号”字段。
(5)在弹出的“表和列”窗口中输入关系名、设置主键表和列名,如图6.5所示,单击“表和列”窗口中的“确定”按钮,再单击“外键关系”窗口中的“确认”按钮,进入如图所示的界面。
(6)单击“保存”按钮,在弹出的“选择名称”对话框中输入关系图的名称。单击“确定”按钮,在弹出的“保存”对话框中单击“是”按钮,保存设置。
2.使用界面方式删除表间的参照关系
如果要删除前面建立的xs表与jy表之间的参照关系,可按以下步骤进行:
(1)在“xsbook”数据库的“数据库关系图”目录下选择要修改的“关系图”,如Diagram_0,右击鼠标,在弹出的快捷菜单中选择“修改”菜单项,打开“数据库关系图设计”窗口。
(2)在“数据库关系图设计”窗口中,选择已经建立的“关系”,单击鼠标右键,选择“从数据库中删除关系”,如图所示。在随后弹出的对话框中,单击“是”按钮,删除表之间的关系。
3.使用SQL命令定义表间的参照关系
(1)创建表的同时定义外键约束
语法格式:
CREATE TABLE <表名>
(
<列名> <数据类型>
[ CONSTRAINT <约束名> ]
[ FOREIGN KEY ][ ( column [ ,...n ] )]
REFERENCES referenced_table_name [ ( ref_column [ ,...n ] ) ]
[ ON DELETE NO ACTION | CASCADE | SET NULL | SET DEFAULT ]
[ ON UPDATE NO ACTION | CASCADE | SET NULL | SET DEFAULT ]
)
说明:
- FOREIGN KEY定义的外键应与参数referenced_table_name指定的主表中的主键或唯一键对应,主表中主键或唯一键字段由参数ref_column指定。主键的数据类型和外键的数据类型必须相同。
- 定义外键时还可以指定参照动作:ON DELETE | ON UPDATE。可以为每个外键定义参照动作。
- 和主键一样,外键也可以定义为列的约束或表的约束。
(2)通过修改表定义外键约束
使用ALTER TABLE语句的ADD子句也可以定义外键约束,语法格式:
ALTER TABLE <表名>
ADD [ CONSTRAINT <约束名>]
[ FOREIGN KEY ][ ( column [ ,...n ] )]
REFERENCES referenced_table_name [ ( ref_column [ ,...n ] ) ]
[ ON DELETE NO ACTION | CASCADE | SET NULL | SET DEFAULT ]
[ ON UPDATE NO ACTION | CASCADE | SET NULL | SET DEFAULT ]
4.使用SQL命令删除表间的参照关系
删除表间的参照关系,实际上删除从表的外键约束即可。
SQL Server索引的创建与维护
创建索引
SQL Server的索引较为常用的有聚集索引、非聚集索引、唯一索引等,为数据表添加合理的索引可以提高数据的查询效率。
CREATE [ UNIQUE ] [ CLUSTERED | NONCLUSTERED ] INDEX index_name
ON <object> ( column [ ASC | DESC ] [ ,...n ] )
[ INCLUDE ( column_name [ ,...n ] ) ]
[ WHERE <filter_predicate> ]
[ WITH ( <relational_index_option> [ ,...n ] ) ]
[ ON { partition_scheme_name ( column_name )
| filegroup_name
| default
}
]
[ FILESTREAM_ON { filestream_filegroup_name | partition_scheme_name | "NULL" } ]
[ ; ]
<object> ::=
{ database_name.schema_name.table_or_view_name | schema_name.table_or_view_name | table_or_view_name }
<relational_index_option> ::=
{
PAD_INDEX = { ON | OFF }
| FILLFACTOR = fillfactor
| SORT_IN_TEMPDB = { ON | OFF }
| IGNORE_DUP_KEY = { ON | OFF }
| STATISTICS_NORECOMPUTE = { ON | OFF }
| STATISTICS_INCREMENTAL = { ON | OFF }
| DROP_EXISTING = { ON | OFF }
| ONLINE = { ON | OFF }
| RESUMABLE = { ON | OFF }
| MAX_DURATION = <time> [MINUTES]
| ALLOW_ROW_LOCKS = { ON | OFF }
| ALLOW_PAGE_LOCKS = { ON | OFF }
| OPTIMIZE_FOR_SEQUENTIAL_KEY = { ON | OFF }
| MAXDOP = max_degree_of_parallelism
| DATA_COMPRESSION = { NONE | ROW | PAGE }
[ ON PARTITIONS ( { <partition_number_expression> | <range> }
[ , ...n ] ) ]
}
<filter_predicate> ::=
<conjunct> [ AND <conjunct> ]
<conjunct> ::=
<disjunct> | <comparison>
<disjunct> ::=
column_name IN (constant ,...n)
<comparison> ::=
column_name <comparison_op> constant
<comparison_op> ::=
{ IS | IS NOT | = | <> | != | > | >= | !> | < | <= | !< }
<range> ::=
<partition_number_expression> TO <partition_number_expression>
【参数】
索引类型 | 描述 |
---|---|
UNIQUE | 为表或视图创建唯一索引,作为唯一索引的字段不可重复且不允许为空 |
CLUSTERED | 为表或视图创建聚集索引,一个表或视图只能存在一个聚集索引 |
NONCLUSTERED | 为表或视图创建非聚集索引 |
无论是使用PRIMARY KEY和UNIQUE约束隐式创建索引,还是使用CREATE INDEX显式创建索引,每个表都最多包含999个非聚集索引
如果未另行指定,默认索引未NONCLUSTERED
【参数】
参数名称 | 描述 |
---|---|
index_name | 索引名称(唯一) |
column | 索引所基于的一列或多列。指定两个或多个列名,可为指定列的组合值创建组合索引。 |
ASC| DESC | 确定特定索引列的升序或降序。默认为ASC |
INCLUDE | 指定要添加到非聚集索引的叶级别的非键列。 非聚集索引可以唯一,也可以不唯一。 |
一个组合索引键中最多可组合 32 列。 组合索引键中的所有列必须在同一个表或视图中。 对于聚集索引,组合索引值允许的最大大小为 900 字节,对于非聚集索引则为 1,700 字节。 对于 SQL 数据库 和 SQL Server 2016 (13.x) 以前的版本,此限制为 16 列和 900 字节。
【包含列】
指定要添加到非聚集索引的叶级别的非键列。 非聚集索引可以唯一,也可以不唯一。
在 INCLUDE 列表中列名不能重复,且不能同时用于键列和非键列。 如果对表定义了聚集索引,则非聚集索引始终包含聚集索引列。
【筛选索引】
通过指定索引中要包含哪些行来创建筛选索引。 筛选索引必须是对表的非聚集索引。 为筛选索引中的数据行创建筛选统计信息。
【指定分区】
语法 | 描述 |
---|---|
partition_scheme_name ( column_name ) | 指定分区方案,根据索引列映射到分区的文件组中。指定的分区索引列必须与分区方案中使用的分区函数参数的数据类型、长度、精度相匹配。 |
filegroup_name | 指定文件组创建指定索引。 |
default | 在表或视图所在的文件组或分区方案上创建指定索引。 |
【索引相关选项】
语法 | 描述 |
---|---|
PAD_INDEX | 是否指定索引填充 ON fillfactor 指定的可用空间百分比应用于索引的中间级页。 OFF或未指定时,考虑到中间级页上的键集,将中间级页填充到接近其容量的程度,以留出足够的空间,使之至少能够容纳索引的最大的一行。 |
FILLFACTOR | 指定索引页的填充率,值为1-100的整数。 |
SORT_IN_TEMPDB | 指定是否在 tempdb 中存储临时排序结果。 |
IGNORE_DUP_KEY | 指定在插入操作尝试向唯一索引插入重复键值时的错误响应。 ON 向唯一索引插入重复键值时将出现警告消息。 只有违反唯一性约束的行才会失败。 OFF 向唯一索引插入重复键值时将出现错误消息。 整个 INSERT 操作将被回滚。 |
STATISTICS_NORECOMPUTE | 指定是否重新计算分布统计信息。 默认为 OFF。 ON 不会自动重新计算过时的统计信息 OFF 启用统计信息自动更新功能。 |
STATISTICS_INCREMENTAL | 为 ON 时,根据分区统计信息创建统计信息。 为 OFF 时,删除统计信息树并且 SQL Server 重新计算统计信息。 默认为 OFF。 |
DROP_EXISTING | 用于删除并重新生成具有已修改列规范的现有聚集或非聚集索引,同时为该索引设置相同的名称。 默认为 OFF。 ON 删除并重新生成指定的现有索引,该索引必须与index_name参数具有相同的名称。 OFF 不删除或重新生成指定的索引,若索引存在则会报错。 |
ONLINE | 指定在索引操作期间基础表和关联的索引是否可用于查询和数据修改操作。 |
RESUMABLE | 指定联机索引操作是否可恢复。(使用对象:SQL Server(从 SQL Server 2019 (15.x) 开始)和 Azure SQL 数据库) |
MAX_DURATION | 指示可恢复联机索引操作在暂停之前执行的时间(以分钟为单位指定的整数值)。(SQL Server(从 SQL Server 2019 (15.x) 开始)和 Azure SQL 数据库) |
ALLOW_ROW_LOCKS | 指定是否允许行锁。 默认值为 ON。 |
ALLOW_PAGE_LOCKS | 指定是否允许使用页锁。 默认值为 ON。 |
OPTIMIZE_FOR_SEQUENTIAL_KEY | 指定是否针对最后一页插入进行优化。 默认为 OFF。 |
MAXDOP | 在索引操作期间替代 max degree of parallelism 配置选项。 有关详细信息,请参阅 配置 max degree of parallelism 服务器配置选项。 使用 MAXDOP 可以限制在执行并行计划的过程中使用的处理器数量。 最大数量为 64 个处理器。 |
DATA_COMPRESSION | 为指定的索引、分区号或分区范围指定数据压缩选项。 无 不压缩索引或分区 row 使用行压缩来压缩索引或分区 page 使用页压缩来压缩索引或分区。 |
聚集索引与非聚集索引
索引是与表或视图关联的磁盘上结构,可以加快从表或视图中检索行的速度。 索引包含由表或视图中的一列或多列生成的键。 这些键存储在一个结构(B 树)中,使 SQL Server 可以快速有效地查找与键值关联的行。
- 聚集
- 聚集索引根据数据行的键值在表或视图中排序和存储这些数据行。 索引定义中包含聚集索引列。 每个表只能有一个聚集索引,因为数据行本身只能按一个顺序存储。
- 只有当表包含聚集索引时,表中的数据行才按排序顺序存储。 如果表具有聚集索引,则该表称为聚集表。 如果表没有聚集索引,则其数据行存储在一个称为堆的无序结构中。
- 非聚集
- 非聚集索引具有独立于数据行的结构。 非聚集索引包含非聚集索引键值,并且每个键值项都有指向包含该键值的数据行的指针。
- 从非聚集索引中的索引行指向数据行的指针称为行定位器。 行定位器的结构取决于数据页是存储在堆中还是聚集表中。 对于堆,行定位器是指向行的指针。 对于聚集表,行定位器是聚集索引键。
聚集索引与非聚集索引可以是唯一,即当前表中索引列不允许重复,也可以不是唯一
当在创建主键时,会默认将主键作为唯一的聚集索引,这里需要注意的是,作为主键的列并不一定适合作为聚集索引。例如现有一张商品信息表,需要要求商品编号是唯一值,则需将商品编号作为唯一值,当时日常的查询并不会按照商品编号查询,而是根据价格范围、购买量或浏览量进行查询或排序,所以此时商品编号作为全表的聚集索引就显得很鸡肋。因此,可以将商品编号作为唯一的非聚集索引,这样即可保障商品编号的唯一性,也可以腾出聚集索引的位置设置更理想的列。
【聚集索引设计】
一般情况下,定义聚集索引键时使用的列越少越好。 考虑具有下列一个或多个属性的列:
-
唯一或包含许多不重复的值
例如,雇员 ID 唯一地标识雇员。 EmployeeID 列的聚集索引或主键约束可提高基于雇员 ID 号搜索雇员信息的查询的性能。 另外,可对 LastName、 FirstName、 MiddleName 列创建聚集索引,因为经常以这种方式分组和查询雇员记录,而且这些列的组合还可提供高区分度。 -
按顺序被访问
例如,产品 ID 唯一地标识 Production.Product 数据库的 AdventureWorks2012 表中的产品。 在其中指定顺序搜索的查询(如 WHERE ProductID BETWEEN 980 and 999)将从 ProductID的聚集索引受益。 这是因为行将按该键列的排序顺序存储。 -
经常用于对表中检索到的数据进行排序
按该列对表进行聚集(即物理排序)是一个好方法,它可以在每次查询该列时节省排序操作的成本。
聚集索引不适用于具有下列属性的列:
-
频繁更改的列
这将导致整行移动,因为 数据库引擎 必须按物理顺序保留行中的数据值。 这一点要特别注意,因为在大容量事务处理系统中数据通常是可变的。 -
宽键
宽键是若干列或若干大型列的组合。 所有非聚集索引将聚集索引中的键值用作查找键。 为同一表定义的任何非聚集索引都将增大许多,这是因为非聚集索引项包含聚集键,同时也包含为此非聚集索引定义的键列。
【非聚集索引设计】
非聚集索引与聚集索引具有相同的 B 树结构,它们之间的显著差别在于以下两点:
- 基础表的数据行不按非聚集键的顺序排序和存储。
- 非聚集索引的叶级别是由索引页而不是由数据页组成。
非聚集索引行中的行定位器或是指向行的指针,或是行的聚集索引键,如下所述:
- 如果表是堆(意味着该表没有聚集索引),则行定位器是指向行的指针。 该指针由文件标识符 (ID)、页码和页上的行数生成。 整个指针称为行 ID (RID)。
- 如果表有聚集索引或索引视图上有聚集索引,则行定位器是行的聚集索引键。
对于索引使用的每个分区,非聚集索引在 index_id >1 的 sys.partitions
中都有对应的一行。 默认情况下,一个非聚集索引有单个分区。 如果一个非聚集索引有多个分区,则每个分区都有一个包含该特定分区的索引行的 B 树结构。 例如,如果一个非聚集索引有四个分区,那么就有四个 B 树结构,每个分区中一个。
重新设计索引键大小较大的非聚集索引,以便只有用于搜索和查找的列为键列。 将覆盖查询的所有其他列设置为包含性非键列。 这样,将具有覆盖查询所需的所有列,但索引键本身较小,而且效率高。
避免添加不必要的列。 添加过多的索引列(键列或非键列)会对性能产生下列影响:
- 一页上能容纳的索引行将更少。 这样会使 I/O 增加并降低缓存效率。
- 需要更多的磁盘空间来存储索引。 特别是,将 varchar(max) 、 nvarchar(max) 、 varbinary(max) 或 xml 数据类型添加为非键索引列会显著增加磁盘空间要求。 这是因为列值被复制到了索引叶级别。 因此,它们既驻留在索引中,也驻留在基表中。
- 索引维护可能会增加对基础表或索引视图执行修改、插入、更新或删除操作所需的时间。
填充因子
提供填充因子选项是为了优化索引数据存储和性能。 当创建或重新生成索引时,填充因子的值可确定每个叶级页上要填充数据的空间百分比,以便在每一页上保留一些剩余空间作为以后扩展索引的可用空间。 例如,指定填充因子的值为 80 表示每个叶级页上将有 20% 的空间保留为空,以便随着向基础表中添加数据而为扩展索引提供空间。 在索引行之间保留可用空间,而不是在索引的末尾保留。
填充因子的值是 1 到 100 之间的百分比,服务器范围的默认值为 0,这表示将完全填充叶级页。
【页拆分】
正确选择填充因子值可提供足够的空间,以便随着向基础表中添加数据而扩展索引,从而降低页拆分的可能性。如果向已满的索引页添加新行, 数据库引擎 将把大约一半的行移到新页中,以便为该新行腾出空间。 这种重组称为页拆分。 页拆分可为新记录腾出空间,但是执行页拆分可能需要花费一定的时间,此操作会消耗大量资源。 此外,它还可能造成碎片,从而导致 I/O 操作增加。 如果经常发生页拆分,可通过使用新的或现有的填充因子值来重新生成索引,从而重新分发数据。 有关详细信息,请参阅 重新组织和重新生成索引。
虽然采用较小的非零填充因子值可减少随着索引的增长而拆分页的需求,但是索引将需要更多的存储空间,并且会降低读取性能。 即使对于面向许多插入和更新操作的应用程序,数据库的读取次数一般也会超过数据库写入次数的 5 到 10 倍。 因此,指定一个不同于默认值的填充因子会降低数据库的读取性能,而降低量与填充因子设置的值成反比。 例如,当填充因子的值为 50 时,数据库的读取性能会降低两倍。 读取性能降低是因为索引包含较多的页,因此增加了检索数据所需的磁盘 I/O 操作。
【将数据添加到表的末尾】
如果新数据在表中均匀分布,则不是 0 或 100 的非零填充因子对性能有利。 但是,如果所有数据都添加到表的末尾,则不会填充索引页中的可用空间。 例如,如果索引键列是 IDENTITY 列,则新行的键将总是增加,并且索引行在逻辑意义上将添加到索引的末尾。 如果将用加长行的大小的数据来更新现有行,则请使用小于 100 的填充因子。 每页上的额外字节将有助于把行中的额外长度造成的页拆分降低到最小限度。
重新组织和重新生成
索引碎片
在B树索引中,当索引包含的页中,索引中的逻辑排序(基于索引中的键值)与索引页的物理排序不匹配时,就存在碎片。
无论何时对基础数据执行插入、更新或删除操作,数据库引擎 都会自动修改索引。 例如,在表中添加行可能会导致拆分行存储索引中的现有页,以腾出空间来插入新行。 随着时间的推移,这些修改可能会导致索引中的数据分散在数据库中(含有碎片)。
对于使用完全或范围索引扫描读取多个页面的查询,碎片多的索引会降低查询性能,因为读取查询所需的数据可能需要额外的 I/O。 查询可能需要大量的小型 I/O 请求来读取相同数量的数据,而不是使用少量的大型 I/O 请求。
存储子系统的顺序 I/O 性能优于随机 I/O 性能时,索引碎片可能会降低性能,因为读取碎片索引需要更多的随机 I/O。
页面密度
数据库中的每个页面包含的行数可以变化。 如果行占用了页面上的所有空间,则页面密度为 100%。 如果页面是空白的,则页面密度为 0%。 如果密度为 100% 的页面拆分为两个页面以容纳新行,则这两个新页面的密度约为 50%。
页面密度较低时,存储相同数量的数据需要更多的页面。 这意味着,读写此数据需要更多的 I/O,缓存此数据需要更多的内存。 内存有限时,要缓存的查询所需页面较少,从而导致磁盘 I/O 增加。 因此,页面密度较低会降低性能。
当 数据库引擎 向页面添加行时,如果索引的填充因子设置为 100 或 0(该值在此上下文中起等效作用)以外的值,则不会完全填充页面。 这会导致页面密度较低,同样会增加 I/O 开销,并降低性能。
页面密度较低可能会增加中间 B 树级别的数量。 这会适度增加在索引扫描和索引查找中找到查找叶级别页面的 CPU 和 I/O 开销。
当查询优化器编译查询计划时,会考虑读取查询所需数据需要的 I/O 开销。 页面密度较低时,需要读取的页面数量更多,因此 I/O 开销更高。 这可能会影响对查询计划的选择。 例如,页面密度因页面拆分而随时间降低时,优化器可能会使用不同的性能和资源消耗配置文件为同一查询编译其他计划。
下面的示例可确定当前数据库中所有行存储索引的平均碎片和页面密度。 它通过 SAMPLED 模式快速返回可操作的结果。 若要获得更准确的结果,请使用 DETAILED 模式。 这需要扫描所有索引页面,并且可能需要很长时间。
SELECT OBJECT_SCHEMA_NAME(ips.object_id) AS schema_name,
OBJECT_NAME(ips.object_id) AS object_name,
i.name AS index_name,
i.type_desc AS index_type,
ips.avg_fragmentation_in_percent,
ips.avg_page_space_used_in_percent,
ips.page_count,
ips.alloc_unit_type_desc
FROM sys.dm_db_index_physical_stats(DB_ID(), default, default, default, 'SAMPLED') AS ips
INNER JOIN sys.indexes AS i
ON ips.object_id = i.object_id
AND
ips.index_id = i.index_id
ORDER BY page_count DESC;
-- Syntax for SQL Server and Azure SQL Database
ALTER INDEX { index_name | ALL } ON <object>
{
REBUILD {
[ PARTITION = ALL ] [ WITH ( <rebuild_index_option> [ ,...n ] ) ]
| [ PARTITION = partition_number [ WITH ( <single_partition_rebuild_index_option> ) [ ,...n ] ]
}
| DISABLE
| REORGANIZE [ PARTITION = partition_number ] [ WITH ( <reorganize_option> ) ]
| SET ( <set_index_option> [ ,...n ] )
| RESUME [WITH (<resumable_index_options>,[...n])]
| PAUSE
| ABORT
}
[ ; ]
<object> ::=
{
{ database_name.schema_name.table_or_view_name | schema_name.table_or_view_name | table_or_view_name }
}
<rebuild_index_option > ::=
{
PAD_INDEX = { ON | OFF }
| FILLFACTOR = fillfactor
| SORT_IN_TEMPDB = { ON | OFF }
| IGNORE_DUP_KEY = { ON | OFF }
| STATISTICS_NORECOMPUTE = { ON | OFF }
| STATISTICS_INCREMENTAL = { ON | OFF }
| ONLINE = {
ON [ ( <low_priority_lock_wait> ) ]
| OFF }
| RESUMABLE = { ON | OFF }
| MAX_DURATION = <time> [MINUTES}
| ALLOW_ROW_LOCKS = { ON | OFF }
| ALLOW_PAGE_LOCKS = { ON | OFF }
| MAXDOP = max_degree_of_parallelism
| DATA_COMPRESSION = { NONE | ROW | PAGE | COLUMNSTORE | COLUMNSTORE_ARCHIVE }
[ ON PARTITIONS ( {<partition_number> [ TO <partition_number>] } [ , ...n ] ) ]
}
<single_partition_rebuild_index_option> ::=
{
SORT_IN_TEMPDB = { ON | OFF }
| MAXDOP = max_degree_of_parallelism
| RESUMABLE = { ON | OFF }
| MAX_DURATION = <time> [MINUTES}
| DATA_COMPRESSION = { NONE | ROW | PAGE | COLUMNSTORE | COLUMNSTORE_ARCHIVE } }
| ONLINE = { ON [ ( <low_priority_lock_wait> ) ] | OFF }
}
<reorganize_option>::=
{
LOB_COMPACTION = { ON | OFF }
| COMPRESS_ALL_ROW_GROUPS = { ON | OFF}
}
<set_index_option>::=
{
ALLOW_ROW_LOCKS = { ON | OFF }
| ALLOW_PAGE_LOCKS = { ON | OFF }
| OPTIMIZE_FOR_SEQUENTIAL_KEY = { ON | OFF }
| IGNORE_DUP_KEY = { ON | OFF }
| STATISTICS_NORECOMPUTE = { ON | OFF }
| COMPRESSION_DELAY= { 0 | delay [Minutes] }
}
<resumable_index_option> ::=
{
MAXDOP = max_degree_of_parallelism
| MAX_DURATION =<time> [MINUTES]
| <low_priority_lock_wait>
}
<low_priority_lock_wait>::=
{
WAIT_AT_LOW_PRIORITY ( MAX_DURATION = <time> [ MINUTES ] ,
ABORT_AFTER_WAIT = { NONE | SELF | BLOCKERS } )
}
【参数】
参数 | 描述 |
---|---|
index_name | 索引名称 |
ALL | 全部索引 |
object | 指定的数据表 |
当指定某一索引名时,只修改指定索引的属性,当指定为ALL时,将会修改该表的所有索引属性。
【重建索引】
当指定REBUILD时,重建索引会将索引列、索引类型、唯一属性和排序顺序重新生成同时会启用已禁用的索引,等同于DBCC DBREINDEX
。
PARTITION | 当索引为分区索引时,指定重新生成或重新组织索引的一个分区,PARTITION = ALL时将重新生成所有分区 |
【禁用索引】
将索引标记为已禁用,从而不能由 数据库引擎使用。 可禁用任何索引。 已禁用的索引的索引定义保留在没有基础索引数据的系统目录中。 禁用聚集索引将阻止用户访问基础表数据。 若要启用索引,请使用 ALTER INDEX REBUILD
或 CREATE INDEX WITH DROP_EXISTING
。
【重新组织】
对于行存储索引,REORGANIZE 指定要重新组织索引叶级别。
当
ALTER INDEX REORGANIZE
使用显式事务(例如,BEGIN TRAN …COMMIT/ROLLBACK 中的ALTER INDEX
)而不是默认隐式事务模式时,REORGANIZE
的锁定行为会变得更加严格,这可能会导致阻塞。
LOB_COMPACTION
- 指定要压缩包含以下这些大型对象 (LOB) 数据类型的数据的所有页面:图像、文本、ntext、varchar(max)、nvarchar(max)、varbinary(max) 和 xml。 压缩这些数据可以减少磁盘上的数据大小。
- 对于聚集索引,这会压缩表中包含的所有 LOB 列。
- 对于非聚集索引,这会压缩作为索引中非键(已包括)列的所有 LOB 列。
REORGANIZE ALL
对所有索引执行LOB_COMPACTION
。 对于每个索引,这会压缩聚集索引、基础表中的所有 LOB 列 或是非聚集索引中的包含列。
Microsoft 建议客户考虑并采用以下索引维护策略:
- 不要假定索引维护始终会显著改进工作负载。
- 衡量重新组织或重新生成索引对工作负载的查询性能的特定影响。 可通过查询存储使用 A/B 测试技术衡量“维护前”和“维护后”的性能。
- 如果发现重新生成索引会提高性能,请尝试将其替换为更新统计信息。 这样可能会获得相似的提升。 在这种情况下,你可能不需要那么频繁地(或完全不需要)重新生成索引,而可以改为定期执行统计信息更新。 若要获得某些统计信息,你可能需要使用 WITH SAMPLE … PERCENT 或 WITH FULLSCAN 子句提高采样率(这种情况并不常见)。
- 随着时间的推移监视索引碎片和页面密度,以确定这些值的升降与查询性能是否相关。 如果增加碎片或降低页面密度导致性能下降到让人无法接受的程度,则重新组织或重新生成索引。 通常,重新组织或重新生成性能下降的查询所使用的特定索引即可。 这样就不必因维护数据库中每个索引产生更高的资源成本。
- 通过在碎片/页面密度和性能之间建立关联,你还可以确定维护索引的频率。 不要假定必须按固定计划执行维护。 更好的策略是监视碎片和页面密度,并在性能下降到不可接受的程度前根据需要运行索引维护。
- 如果已确定需要维护索引并且可接受其资源成本,则在资源使用率低的时段(如果存在这样的时段)内执行维护,并牢记资源使用模式可能会随时间而变化。
描述的聚集索引和非聚集索引
SQL Server 索引体系结构和设计指南
CREATE INDEX(Transact-SQL)
ALTER INDEX (Transact-SQL)
优化索引维护以提高查询性能并减少资源消耗
以上是关于SQL server速成之路索引与数据完整性的主要内容,如果未能解决你的问题,请参考以下文章
SQL Server 2008 和 Visual Web Developer 2008 速成版:连接到数据库失败!