SQL Server 聚集索引(clustered index)和非聚集索引(nonclustered index)

我们可以把索引理解为一种特殊的目录。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上仍然可以走索引。

SQL Server 聚集索引(clustered index)和非聚集索引(nonclustered index)_第1张图片
上面的图作为参考:引用自https://blog.csdn.net/riemann_/article/details/90324846

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

--select     
    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_id
WHERE OBJECTPROPERTY(s.[object_id], 'IsUserTable') = 1
AND 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
SQL Server 聚集索引(clustered index)和非聚集索引(nonclustered index)_第2张图片
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 id

OPEN authors_cursor

FETCH NEXT FROM authors_cursor  INTO @name

WHILE @@FETCH_STATUS = 0 
BEGIN    

 DBCC DBREINDEX (@name, '', 90)

 FETCH NEXT FROM authors_cursor     INTO @name 
END

deallocate 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子句中的搜索条件并确定哪个索引能缩小表扫描的搜索空间,它能实现自动优化。

你可能感兴趣的:(SQL)