博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
SQL Server 聚集索引(clustered index)和非聚集索引(nonclustered index)
阅读量:4042 次
发布时间:2019-05-24

本文共 3756 字,大约阅读时间需要 12 分钟。

我们可以把索引理解为一种特殊的目录。SQL SERVER提供了:聚集索引(clustered index)和非聚集索引(nonclustered index)。

我们一般把常出现在 WHERE , Group , Order 后面的字段考虑作为索引字段.

一,把这种正文内容本身就是一种按照一定规则排列的目录称为“聚集索引”,根据该索引可以直接找到目录。例如新华字典里的字是按拼音排序的.

1.聚集索引一个表只能有一个.

2.我们默认都会在主键字段上建立聚集索引,理论上聚集索引可以创建在任何一列你想创建的字段上,也就是该字段不需要是唯一值,但实际情况并不能随便指定,否则在性能上会很糟糕。
3.主键就是聚集索引–错误想法的.虽然默认是在主键上建立聚集索引的。

通常,我们会在每个表中都建立一个自增长ID列。如果我们将这个列设为主键,此列默认为聚集索引。这样做有好处,就是可以让您的数据在数据库中按照ID进行物理排序,但意义不大,是对聚集索引的一种浪费。

显而易见,聚集索引的优势是很明显的,而每个表中只能有一个聚集索引的规则,这使得聚集索引变得更加珍贵。

从我们前面谈到的聚集索引的定义我们可以看出,使用聚集索引的最大好处就是能够根据查询要求,迅速缩小查询范围。在实际应用中,因为 ID号是自动生成的,我们并不知道每条记录的ID号,所以我们很难在实践中用ID号来进行查询(可能有的系统在修改记录时定位会用到)。这就使让ID号这个主键作为聚集索引成为一种资源浪费。其次,让每个ID号都不同的字段作为聚集索引也不符合“大数目的不同值情况下不应建立聚合索引”规则。

如在OA系统中,进行数据查询都很多都是 发文日期,用户名,分类.那么把“发文日期”作为聚集索引应该 比ID字段要更有意义.

二,非聚集索引一个表可以存在多个.我们把这种目录纯粹是目录,正文纯粹是正文的排序方式称为“非聚集索引”。

  查询时先在目录中查找(有可能不止一个目录,例如按部首查字里,先在部首目录中找,再到笔画目录中找),再转到正文.

无论聚集索引还是非聚集索引,我们都可以多个字段进行组合索引,有个重要的原则是"最左前缀匹配原则":无论您是否经常使用聚合索引的其他列,但其前导列一定要是使用最频繁的列。

联合索引中的字段,只有某个字段左边的所有字段都被使用了,才能使用该字段上的索引。例如,有索引 INDEX idx_i1(col_a,col_b),如果查询条件为 wherecol_b =1,则无法使用索引 idx_i1。
  但是联合索引中的字段,即使某个字段右边的其他字段没有被使用,该字段之前(含)的所有字段仍然可以正常使用索引。例如,有索引 INDEX idx_i2(col_a,col_b,col_c),则查询条件 wherecol_a =1 and col_b =2在字段 col_a和 col_b上仍然可以走索引。

在这里插入图片描述

上面的图作为参考:引用自https://blog.csdn.net/riemann_/article/details/90324846

三,以下这些语句可以参考索引是否建得合理(当然您的项目数据库要有一定的使用时间积累后才更准确).

--1select         DB_NAME(d.database_id) DbName,        OBJECT_NAME(d.object_id)TbName,       i.name AS IndexName,        user_seeks,        user_scans,        user_lookups,                user_updates    from sys.dm_db_index_usage_stats d    inner join sys.indexes i            on d.object_id = i.object_id               and d.index_id = i.index_id    where database_id = DB_ID('Name of Database') --找到低效索引--找到user_updates比较大,而user_seek、user_scan很小的索引,分析一下为什么有维护的开销,而实际索引使用次数较少,是不是索引建的不合理.--2.SELECT OBJECT_NAME(s.[object_id]) AS [Table Name] ,i.name AS [Index Name] ,i.index_id ,user_updates AS [Total Writes] ,user_seeks + user_scans + user_lookups AS [Total Reads] ,user_updates - ( user_seeks + user_scans + user_lookups )AS [Difference]FROM sys.dm_db_index_usage_stats AS s WITH ( NOLOCK )INNER JOIN sys.indexes AS i WITH ( NOLOCK )ON s.[object_id] = i.[object_id]AND i.index_id = s.index_idWHERE OBJECTPROPERTY(s.[object_id], 'IsUserTable') = 1AND s.database_id = DB_ID()AND user_updates > ( user_seeks + user_scans + user_lookups )AND i.index_id > 1 --and OBJECT_NAME(s.[object_id])='Lot'ORDER BY [Difference] DESC ,[Total Writes] DESC ,[Total Reads] ASC ;--结果描述,倒数2,3列, Total Write 与 Total Reads如果 Total Reads 等于 0,说明索引没起作用,建议删除当前索引

四,平时数据库维护过程中查看索引碎片及索引重新生成也可考虑.

1.查看表的索引碎片信息。
DBCC SHOWCONTIG(表名) WITH ALL_INDEXES
在这里插入图片描述
2.索引重新生成

对某个表重建索引:dbcc dbreindex('表名',pk_索引名,100) --pk_索引名 如果为'' 表示该表的所有索引,100是期望能达到的值,一般90-100都可以.--对数据库所有表进行重建索引USE My_Database; DECLARE @name varchar(100)DECLARE authors_cursor CURSOR FOR  Select [name]   from sysobjects where xtype='u' order by idOPEN authors_cursorFETCH NEXT FROM authors_cursor  INTO @nameWHILE @@FETCH_STATUS = 0 BEGIN     DBCC DBREINDEX (@name, '', 90) FETCH NEXT FROM authors_cursor     INTO @name ENDdeallocate authors_cursor

五,影响索引失效的一些查询条件

这个网上很多资料,例如
1)like ‘ABC%’ 索引有效; like ‘%ABC%’, like ‘%ABC’ – 索引失效
2)其它 OR , ,NOT、!=、<>、NOT EXISTS、NOT IN、NOT LIKE等,另外还有函数等.
六,在查询和提取超大容量的数据时,影响数据库响应时间的最大因素不是数据查找,而是物理的I/0操作。

例如表data_logg表是千万记录级别的表,以下查询返回约40万条.

select * from data_log where LogDate Between ‘2020-01-01’ AND ‘2020-12-31’
–测试发现 LogDate 建立聚集索引\非聚集索引\无索引 耗时几乎一样.这种情况 相对于IO操作的时间,使用索引提升的时间可以忽略.

这里只是讲一个特例.当然合理建立索引非常必要!

七,其它

1. 多查询条件的先后摆放问题
select * from user where name=’‘abc’’ and uid> 10000
和执行:
select * from user where uid > 10000 and name=’‘abc’’

有人担心以上两条语句的执行效率是否一样,因为如果简单的从语句先后上看,有个条件筛选范围先后的问题.

  事实上,担心不必要。sql server中有一个“查询分析优化器”,它可以计算出where子句中的搜索条件并确定哪个索引能缩小表扫描的搜索空间,它能实现自动优化。

你可能感兴趣的文章
C++ this指针(2) - this指针的类型
查看>>
C++ this指针(3) - 删除this指针
查看>>
C++构造与析构(1) - 构造函数
查看>>
C++构造与析构(2) - 拷贝构造函数
查看>>
C++构造与析构(3) - 析构函数
查看>>
C++构造与析构(4) - 默认构造函数
查看>>
C++构造与析构(5) - 何时必须自定义拷贝构造函数
查看>>
C++构造与析构(7) - 数据成员的初始化
查看>>
C++构造与析构(8) - 什么时候必须使用初始化列表
查看>>
C++构造与析构(9) - 默认构造函数
查看>>
C++构造与析构(10) - private析构函数
查看>>
C++构造与析构(11) - 析构函数的行为
查看>>
C++构造与析构(12) - copy elision编译器优化
查看>>
C++构造与析构(13) - 内建类型的默认构造函数
查看>>
C++构造与析构(14) - 编译器何时创建默认构造函数和拷贝构造函数
查看>>
C++构造与析构(15) - 为何拷贝构造函数必须为const
查看>>
C++构造与析构(16) - virtual构造函数
查看>>
C++构造与析构(17) - virtual拷贝构造函数
查看>>
C++构造与析构(18) - 静态对象(static object)何时销毁
查看>>
C++构造与析构(19) - 显式地调用构造函数和析构函数
查看>>