SqlServer数据库索引 学习记录

什么是索引?

数据库中的索引是:
某个表中一列 或 多列值的集合和相应的指向表中物理标识这些值的数据页的 逻辑指针清单。

数据库索引有哪几种?

聚集索引:
一张表只能有一个聚集索引,通常主键默认是聚集索引,也可以设置非主键列为聚集索引,设置聚集索引后,数据存储将按照索引列有规则的排列。

非聚集索引(NONCLUSTERED):
一张表可有多个,表中各行的物理顺序与键值的逻辑(索引)顺序不匹配。



单表索引的数目一般不要超过3个,最多不要超过5个。
一张表最好不要超过5个索引,一个索引最多有16个索引列。
每当表数据变化(增、删、改)的时候,每个索引都需要按照规则更新索引位置。



创建索引的标准:
用语频繁搜索的列;用语对数据进行排序的列。

注意:如果表中仅有几行,或列中只包含几个不同的值,不推荐创建索引,因为SQL Server 在小型表中用索引搜索数据所花的时间比逐行搜索更长。

哪些情况不要使用索引?

1)重复值较多的列不要建立索引,比如用户表的性别字段,最多三种值(男、女、未知)。
2)text、image、byte[]列 不要建立索引
3)更新频繁的列不要建立索引,因为如果该列有索引,那么在更新值的时候同时需要更新索引,这样会导致查询变快,更新变慢。

索引的一些使用规则?

查询网址(包含一些数据库性能优化):https://www.cnblogs.com/lihuali/p/5899255.html

这里记录一些自己学习的知识点。

1.
比如like '张%' 就是符合SARG(符合扫描参数)标准,而like '%张' 就不符合该标准。

通配符 % 在字符串首字符的使用会导致 索引无法使用,虽然实际应用中很难避免这样用,但还是应该对这种现象有所了解,至少知道此种用法性能是很低下的。

2.
不同类型的索引效能是不一样的,应尽可能先使用效能高的。

比如:(数字类型的索引查找效率)高于(字符串类型),定长字符串char,nchar的索引效率高于变长字符串varchar,nvarchar的索引。

应该将
where username='张三' and age>20
改进为
where age>20 and username='张三'

注意:此处,SQL的查询分析优化功能可以做到自动重排条件顺序,但还是建议预先手工排列好。

3.
例:表stuff 有 200000行,id_no上有非群集索引,请看下面这个SQL:
select count(*) from stuff where id_no in(′0′,′1′) (23秒)

我们期望它会根据每个or子句分别查找,再将结果相加,这样可以利用id_no上的索引;

但实际上,它却采用了"OR策略",即先取出满足每个or子句的 行,存入临时数据库的工作表中,再建立唯一索引以去掉重复行,最后从这个临时表中计算结果。

因此,实际过程没有利用id_no 上索引,并且完成时间还要 受tempdb数据库性能的影响。

实践证明,表的行数越多,工作表的性能就越差,当stuff有620000行时,执行时间会非常长!如果确定不同的条件不会产生大量重复值,还不如将or子句分开:

select count(*) from stuff where id_no=′0′
select count(*) from stuff where id_no=′1′

得到两个结果,再用union作一次加法合算。因为每句都使用了索引,执行时间会比较短,

select count(*) from stuff where id_no=′0′
union
select count(*) from stuff where id_no=′1′

从实践效果来看,使用union在通常情况下比用or的效率要高的多,而exist关键字和in关键字在用法上类似,性能上也类似,都会产生全表扫描,效率比较低下,根据未经验证的说法,exist可能比in要快些。

like关键字支持通配符匹配,但这种匹配特别耗时。
例如:select * from customer where zipcode like “21_ _ _”,即使在zipcode字段上已建立了索 引,在这种情况下也可能还是采用全表扫描方式。
如果把语句改 为:select * from customer where zipcode >“21000”,在执行查询时就会利用索引,大大提高速度。但这种变通是有限制的,不应引起业务意义上的损失,对于邮政编码而言,zipcode like “21_ _ _” 和 zipcode >“21000” 意义是完全一致的。

4.
order by按 聚集索引列 排序效率最高
排序是较耗时的操作,应尽量简化或避免对大型表进行排序,如缩小排序的列的范围,只在有索引的列上排序等等。
我们来看:(gid是主键,fariqi是聚合索引列)
select top 10000 gid,fariqi,reader,title from tgongwen
用时:196 毫秒。 扫描计数 1,逻辑读 289 次,物理读 1 次,预读 1527 次。

select top 10000 gid,fariqi,reader,title from tgongwen order by gid asc
用时:4720毫秒。 扫描计数 1,逻辑读 41956 次,物理读 0 次,预读 1287 次。

select top 10000 gid,fariqi,reader,title from tgongwen order by gid desc
用时:4736毫秒。 扫描计数 1,逻辑读 55350 次,物理读 10 次,预读 775 次。

select top 10000 gid,fariqi,reader,title from tgongwen order by fariqi asc
用时:173毫秒。 扫描计数 1,逻辑读 290 次,物理读 0 次,预读 0 次。

select top 10000 gid,fariqi,reader,title from tgongwen order by fariqi desc
用时:156毫秒。 扫描计数 1,逻辑读 289 次,物理读 0 次,预读 0 次。

同时,按照某个字段进行排序的时候,无论是正序还是倒序,速度是基本相当的。

5.

节省数据查询系统开销方面的措施?

(1)使用TOP尽量减少取出的数据量。

(2)字段提取要按照“需多少、提多少”的原则,避免“select *”
字段大小越大,数目越多,select所耗费的资源就越多,比如取int类型的字段就会比取char的快很多。
我们每少提取一个字段,数据的提取速度就会有相应的提升。提升的幅度根据舍弃的字段的大小来判断

(3)count(*) 与 count(字段) 方法比较
用 count(*)和用 count(主键)的速度是相当的,而count(*)却比其他任何除主键以外的字段汇总速度要快,而且字段越长,汇总速度就越慢。
如果用 count(*), SQL SERVER会自动查找最小字段来汇总。当然,如果您直接写count(主键)将会来的更直接些。

(4)有嵌套查询时,尽可能在内层过滤掉数据
如果一个列同时在主查询和where子句中出现,很可能当主查询中的列值改变之后,子查询必须重新查询一次。
而且查询嵌套层次越多,效率越低,因此应当尽量避免子查询。如果子查询不可避免,那么要在子查询中过滤掉尽可能多的行

(5)多表关联查询时,需注意表顺序,并尽可能早的过滤掉数据
在使用Join进行多表关联查询时候,应该使用系统开销最小的方案。连接条件要充份考虑带有索引的表、行数多的表,并注意优化表顺序;说的简单一点,就是尽可能早的将之后要做关联的数据量降下来。

Sql语句集中:

--索引操作学习网址:https://www.cnblogs.com/knowledgesea/p/3672099.html

--建一张无主键表B
--CREATE TABLE B(
--			Id			varchar(36) NOT NULL,
--			IndexCol1	varchar(50) NULL,
--			IndexCol2	varchar(50) NULL,
--			IndexCol3	varchar(50) NULL
--)

--设置表B的主键为Id,并设置主键列为聚集索引 PK_ID
--ALTER TABLE B ADD CONSTRAINT PK_ID PRIMARY KEY(Id)

--设置B表的IndexCol1字段列为非聚集索引 non_index_col1
--CREATE NONCLUSTERED INDEX non_index_col1 ON B(IndexCol1)

--查看索引 non_index_col1 是否存在(其它表可能也有叫这个名字的索引)
--SELECT * FROM SYSINDEXES WHERE NAME='non_index_col1'
--IF EXISTS (SELECT * FROM SYSINDEXES WHERE NAME='non_index_col1')
--PRINT '存在'

--插入5条数据
--INSERT INTO B(Id,IndexCol1,IndexCol2,IndexCol3) VALUES
--(NEWID(), '第1行第1列', '第1行第2列', '第1行第3列'),
--(NEWID(), '第2行第1列', '第2行第2列', '第2行第3列'),
--(NEWID(), '第3行第1列', '第3行第2列', '第3行第3列'),
--(NEWID(), '第4行第1列', '第4行第2列', '第4行第3列'),
--(NEWID(), '第5行第1列', '第5行第2列', '第5行第3列')

--删除2行记录
--DELETE FROM B WHERE
--	IndexCol1='第2行第1列' OR
--	IndexCol1='第4行第1列'

--查看表的索引情况,当碎片显示比较高时,就可以重建索引,消除碎片了
--本例插入,删除的记录不多,所以碎片率很低
--DBCC showcontig('B')

--重建索引
--DBCC DBREINDEX('B')

--创建好索引后,普通查询语句 默认按IndexCol1列对应的索引non_index_col1进行查询。
--SELECT * FROM B WHERE IndexCol1='第1行第1列'
--Go


 

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章