SQL Server索引误区使用建议
常见的误区:
1.数据库不需要索引
2.主键总是聚集的
3.联机索引操作不引起阻塞
4.复合索引下列的顺序不重要
5.聚集索引以物理顺序存储
6.填充因子可以应用在索引的插入过程中
7.每个表应该有聚集索引
一:数据库不需要索引
--生成堆表
SELECT * INTO MythOne FROM Sales.SalesOrderDetail
--统计查询所用的I/O
SET STATISTICS IO ON
SET NOCOUNT ON
GO
SELECT salesorderID,SalesOrderDetailID,CarrierTrackingNumber,OrderQty,productID,SpecialOfferID,
UnitPrice,UnitPriceDiscount,LineTotal FROM MythOne WHERE CarrierTrackingNumber='4911-403c-98'
SET STATISTICS IO OFF
在CarrierTrackingNumber创建一个索引,以提高查询性能
CREATE INDEX IX_CarrierTrackingNumber ON mythone(CarrierTrackingNumber)
GO
SET STATISTICS IO ON
SET NOCOUNT ON
GO
SELECT salesorderID,SalesOrderDetailID,CarrierTrackingNumber,OrderQty,productID,SpecialOfferID,
UnitPrice,UnitPriceDiscount,LineTotal FROM MythOne WHERE CarrierTrackingNumber='4911-403c-98'
GO
SET STATISTICS IO OFF
--表 'MythOne'。扫描计数 1,逻辑读取 15 次,物理读取 0 次,预读 0 次,lob 逻辑读取 0 次,lob 物理读取 0 次,lob 预读 0 次。
二:主键总是聚集的
当你创建一个主键,默认就会在上面自动创建一个聚集索引,主键的目的是为了保持数据的唯一性、从逻辑上组织数据
--创建测试表
CREATE TABLE dbo.MythoTwo1(
RowID INT NOT NULL,
Column1 NVARCHAR(128),
Column2 NVARCHAR(128)
)
--创建具有聚集特性的主键
ALTER TABLE dbo.MythoTwo1
ADD CONSTRAINT PK_MythoTwo1 PRIMARY KEY (RowID)
GO
CREATE TABLE dbo.MythTwo2(
RowID INT NOT NULL,
Column1 NVARCHAR(128),
Column2 NVARCHAR(128)
)
--先创建聚集索引
CREATE CLUSTERED INDEX INDEX_CL_MythTwo2 ON dbo.MythTwo2(rowid)
--添加主键
ALTER TABLE dbo.MythTwo2
ADD CONSTRAINT PK_MythTwo2 PRIMARY KEY (RowID)
GO
--检查两个表的索引类型
SELECT OBJECT_NAME(object_id) AS table_name,
name, index_id,type,type_desc,is_unique,is_primary_key
FROM sys.indexes
WHERE OBJECT_ID IN (OBJECT_ID('dbo.MythoTwo1'),OBJECT_ID('dbo.MythoTwo2'))
MythoTwo1只有一个聚集索引,并且是唯一的(is_unique)和聚集的(is_primary_key);而MythoTwo2上有两个索引,一个是单纯的聚集索引,一个是唯一且为主键的非聚集索引 3.联机索引操作不引起阻塞 SQL Server 会在表中受联机索引操作影响的数据上加上意向共享锁,并在最终更新原有索引时对这部分数据架构更新锁或者共享锁,然后阻塞其他事务的修改操作 4.复合索引下,列的顺序不重要
一个索引通常不会使用到表中的所有列,并且列的顺序也是有意义的,复合索引中最左的一列有统计信息,其他列不计算统计信息,这就说明索引的列是有顺序,而且非常重要。
--创建表
SELECT SalesOrderID,orderdate,DueDate,ShipDate
INTO dbo.MythFour
FROM Sales.SalesOrderHeader; --添加一个聚集索引
ALTER TABLE dbo.mythFour
ADD CONSTRAINT PK_MythFour PRIMARY KEY CLUSTERED(SalesOrderID);
--添加由三列组成的非聚集索引
CREATE NONCLUSTERED INDEX IX_MythFour ON dbo.MythFour(orderdate,DueDate,ShipDate); --执行查询索引最左边
SELECT orderdate FROM dbo.MythFour WHERE orderdate='2008-07-17 00:00:00.000' --使用索引最右边
SELECT orderdate FROM dbo.MythFour WHERE ShipDate='2008-07-17 00:00:00.000'
一个是索引扫描,一个是索引查找,因为前面已经说过,除了索引最左侧的列有统计信息之外,其他列没有,所以用其他列,SQLServer 无法预估行数,只能使用扫描来遍历索引
5.聚集索引以物理顺序存储
--创建测试表
CREATE TABLE dbo.MythFive
(
rowid INT PRIMARY KEY CLUSTERED,
TestValue VARCHAR(20) NOT NULL
) INSERT INTO dbo.MythFive(rowid,TestValue)
VALUES(1,'FirstRecordAdded');
INSERT INTO dbo.MythFive(rowid,TestValue)
VALUES(3,'SecondRecordAdded');
INSERT INTO dbo.MythFive(rowid,TestValue)
VALUES(2,'ThirdRecordAdded'); --检查索引情况
DBCC IND ('AdventureWorks2014','dbo.MythFive',1)
MythFive的索引情况:
检查PageType=1即数据页的情况
DBCC TRACEON(3604);
GO
DBCC PAGE(AdventureWorks2014,1,140107,2)
聚集索引并不是按物理顺序存放数据
6.填充因子可以应用在索引的插入过程中
填充因子用于创建(不是指第一次初始化,而是使用create index with drop_existing这种方式,第一次初始化的时候会尽量填满页)、重建或者重组索引。
--创建测试表
CREATE TABLE dbo.MythSix
(
RowID INT NOT NULL,
Columnl VARCHAR(500)
);
--创建聚集索引,填充因子为50,即只填满页的一半就是分页
ALTER TABLE dbo.MythSix ADD CONSTRAINT
PK_MythSix PRIMARY KEY CLUSTERED (RowID) WITH(FILLFACTOR=50); --制造测试数据
WITH L1(z) AS (SELECT 0 UNION ALL SELECT 0)
, L2(z) AS (SELECT 0 FROM L1 a CROSS JOIN L1 b)
, L3(z) AS (SELECT 0 FROM L2 a CROSS JOIN L2 b)
, L4(z) AS (SELECT 0 FROM L3 a CROSS JOIN L3 b)
, L5(z) AS (SELECT 0 FROM L4 a CROSS JOIN L4 b)
, L6(z) AS (SELECT TOP 1000 0 FROM L5 a CROSS JOIN L5 b) INSERT INTO dbo.MythSix
SELECT ROW_NUMBER() OVER(ORDER BY z) AS RowID,REPLICATE('X',500) FROM L6 --检查索引的可用情况
SELECT object_id,index_id,avg_page_space_used_in_percent
FROM sys.dm_db_index_physical_stats(DB_ID(),OBJECT_ID('dbo.MythSix'),NULL,NULL,'DETAILED') WHERE index_level=0
执行结果:
可用空间并不是50,可见填充因子在插入时并未起作用。实际上,插入数据时,SQL Server依旧会尽量填满一页在分配新页。为了证明在重建、重组时填充因子会起作用。可以重建这个表
ALTER TABLE dbo.MythSix rebuild
这时候填充因子才生效,但这些配置并不是绝对的,也就是说不一定设了50就真的是正好50
7.每个表应该有聚集索引
不使用聚集索引的理由:
1.碎片问题增加I/O操作
2.当因为数据修改而产生page split时,会引起聚集索引上多条记录位置变更
3.过渡的key lookup会引起额外的I/O开销,这通常是索引设计的问题
不使用堆的理由:
1.过多的forwarded records会导致额外的I/O,降低性能
2.没有办法通过移除forwarded records来维护堆表
3.非聚集索引往往可能要进行多余的排序操作
选择是否用聚集索引时,可以考虑以下几点:
1.表上是否有唯一、自增的键值。
2.表上是否有高度唯一的列
3是否经常有范围查询
堆表可以比聚集索引更好的性能表现:
1.高频率的增删操作
2.键值经常改变,特别是索引上的位置改变
3.插入大量数据到列中
4.主键值并不自增或者唯一
索引使用建议
1.保留主键创建中的聚集索引选项
由于聚集索引合适建立在唯一、自增的列或者多列上而这些特性在主键中能够得到满足,所以SQL Server 创建主键时默认就是聚集索引。
2.平衡索引的个数
3.填充因子
(1)数据库层面的填充因子用于控制索引创建、维护过程中索引页保留空余空间的默认值。往往数据库层面的填充因子不建议修改,如有必要,可以修改索引层面上的填充因子
(2)如果索引上存在严重的碎片问题,在索引层面上调整填充因子可以在一定程度上减少碎片问题
4.在外键列加索引
创建外键列后,强烈建议在外键列上加索引,这可以帮助父表和子表关联时提高性能
关于索引的查询建议
1.Like 把like 的这列数据进行索引化,把核心的数据或者常用数据当成一个列
SET STATISTICS IO ON ;
SELECT AddressID,AddressLine1,AddressLine2,City,StateProvinceID,PostalCode FROM Person.Address
WHERE AddressLine1 LIKE '%Cynthia%'
表 'Address'。扫描计数 1,逻辑读取 216 次,物理读取 0 次,预读 0 次,lob 逻辑读取 0 次,lob 物理读取 0 次,lob 预读 0 次。
创建一个全文索引:
CREATE FULLTEXT CATALOG ftQueryStrategies AS DEFAULT;
CREATE FULLTEXT INDEX ON person.Address(AddressLine1)
KEY INDEX PK_Address_AddressID;
GO
SET STATISTICS IO ON;
SELECT AddressID,AddressLine1,AddressLine2,City,StateProvinceID,PostalCode FROM Person.Address
WHERE CONTAINS (AddressLine1,'Cynthia')
表 'Address'。扫描计数 0,逻辑读取 12 次,物理读取 0 次,预读 0 次,lob 逻辑读取 0 次,lob 物理读取 0 次,lob 预读 0 次。
串联:
在查询过程中,很多时候需要把某些列串联起来作为新列,特别是在where条件中,使用了FirstName+LastName=‘***’,会导致索引无效
SET STATISTICS IO ON;
CREATE INDEX IX_PersonContact_FirstNameLastName ON Person.Person(FirstName,LastName)
GO
SELECT BusinessEntityID,FirstName,LastName FROM Person.Person WHERE FirstName+' '+ LastName='Gustavo Achong'
表 'Person'。扫描计数 1,逻辑读取 99 次,物理读取 0 次,预读 0 次,lob 逻辑读取 0 次,lob 物理读取 0 次,lob 预读 0 次。
有两种解决方案:
1.使用计算列,把两个列上的值预先存储,然后在计算列上加上索引
2.改写查询
--使用计算列
ALTER TABLE Person.Person ADD Name AS Firstname+''+ lastname
CREATE NONCLUSTERED INDEX IX_PersonContact_Name ON Person.Person(Name)
--用新列查询
SET STATISTICS IO ON;
SELECT BusinessEntityID,FirstName,LastName FROM Person.Person WHERE name='Gustavo Achong'
表 'Worktable'。扫描计数 0,逻辑读取 0 次,物理读取 0 次,预读 0 次,lob 逻辑读取 0 次,lob 物理读取 0 次,lob 预读 0 次。
表 'Person'。扫描计数 1,逻辑读取 2 次,物理读取 0 次,预读 0 次,lob 逻辑读取 0 次,lob 物理读取 0 次,lob 预读 0 次。
--第二种情况
DROP INDEX IX_PersonContact_Name ON Person.Person
SET STATISTICS IO ON ;
SELECT BusinessEntityID,FirstName,LastName FROM Person.Person WHERE FirstName='Gustavo' AND LastName='Achong'
表 'Person'。扫描计数 1,逻辑读取 2 次,物理读取 0 次,预读 0 次,lob 逻辑读取 0 次,lob 物理读取 0 次,lob 预读 0 次。
3.标量函数
使用函数,特别是where条件和on条件中使用标量函数,也会对索引带来影响。标量函数会使列上的值在查询过程中“变成”另一个值,索引上面的统计信息就会失效,进而导致优化器选择扫描操作
SELECT BusinessEntityID,FirstName,LastName FROM person.Person
WHERE FirstName='Gustavo'
SELECT BusinessEntityID,FirstName,LastName FROM Person.Person
WHERE UPPER(FirstName)='Gustavo'
4.数据类型转换
--4.数据类型转换
--创建示例表
SELECT BusinessEntityID
,CAST(FirstName AS VARCHAR(50)) AS FirstName
,CAST(MiddleName AS VARCHAR(50)) AS MiddleName
,CAST(LastName AS VARCHAR(50)) AS LastName
INTO PersonPerson
FROM Person.Person; CREATE CLUSTERED INDEX IX_PersonPerson_ContactID ON PersonPerson(BusinessEntityID);
GO
SET STATISTICS IO ON DECLARE @FirstName nvarchar(100) --注意类型
SET @FirstName ='Katherine';
--提高带参数执行SQL语句的索引效率 OPTION(RECOMPILE)
SELECT * FROM PersonPerson WHERE @FirstName=@FirstName OPTION(RECOMPILE); GO
DECLARE @FirstName VARCHAR(100)--注意类型型,这里不存在类型转换,均为varchar
SET @FirstName='Katherine';
SELECT * FROM PersonPerson
WHERE FirstName=@FirstName
表 'PersonPerson'。扫描计数 1,逻辑读取 89 次,物理读取 0 次,预读 0 次,lob 逻辑读取 0 次,lob 物理读取 0 次,lob 预读 0 次。
数据类型转换和标量函数的影响类似,都会使得统计信息丢失,从而使索引无效,所以应该尽可能保持WHERE、ON条件两边的类型相等。
SQL Server索引误区使用建议的更多相关文章
- SQL Server索引总结二
从CREATE开始 通过显式的CREATE INDEX命令 在创建约束时作为隐含的对象 随约束创建的隐含索引 当向表中添加如下两种约束之一时,就会创建隐含索引. 主键约束(聚集索引) 唯一约束(唯一索 ...
- 转: SQL Server索引的维护 - 索引碎片、填充因子
转:http://www.cnblogs.com/kissdodog/archive/2013/06/14/3135412.html 实际上,索引的维护主要包括以下两个方面: 页拆分 碎片 这两个问题 ...
- SQL Server 索引结构及其使用(一)
转载:SQL Server 索引结构及其使用(一) 作者:freedk 一.深入浅出理解索引结构 实际上,您可以把索引理解为一种特殊的目录.微软的SQL SERVER提供了两种索引:聚集索引(clus ...
- SQL Server索引设计 <第五篇>
SQL Server索引的设计主要考虑因素如下: 检查WHERE条件和连接条件列: 使用窄索引: 检查列的选择性: 检查列的数据类型: 考虑列顺序: 考虑索引类型(聚集索引OR非聚集索引): 一.检查 ...
- SQL Server索引的维护 - 索引碎片、填充因子 <第三篇>
实际上,索引的维护主要包括以下两个方面: 页拆分 碎片 这两个问题都和页密度有关,虽然两者的表现形式在本质上有所区别,但是故障排除工具是一样的,因为处理是相同的. 对于非常小的表(比64KB小得多), ...
- SQL Server索引进阶:第九级,读懂执行计划
原文地址: Stairway to SQL Server Indexes: Level 9,Reading Query Plans 本文是SQL Server索引进阶系列(Stairway to SQ ...
- SQL Server 索引维护(1)——系统常见的索引问题
前言: 在很多系统中,比如本人目前管理的数据库,索引经常被滥用,甚至使用DTA(数据库引擎优化顾问)来成批创建索引(DTA目前个人认为它的真正用处应该是在发现缺失的统计信息,在以前的项目中,用过一次D ...
- SQL Server 索引碎片产生原理重建索引和重新组织索引
数据库存储本身是无序的,建立了聚集索引,会按照聚集索引物理顺序存入硬盘.既键值的逻辑顺序决定了表中相应行的物理顺序 多数情况下,数据库读取频率远高于写入频率,索引的存在 为了读取速度牺牲写入速度 页 ...
- [转帖]SQL Server 索引中include的魅力(具有包含性列的索引)
SQL Server 索引中include的魅力(具有包含性列的索引) http://www.cnblogs.com/gaizai/archive/2010/01/11/1644358.html 上个 ...
随机推荐
- C# 基础之string[,] 和string[][]
昨天做项目时碰到一个问题,后台返回一张关系表,里面就两个字段,简化为A,B字段(1:N的关系),一个A对应多个B字段. 由于对于string[,] 和string[][] 的认识不到位,刚开始搞错了数 ...
- DUILIB入门简明教程
电子书下载: DUILIB入门简明教程.chm 文章作者: Alberl 电子书制作: 邓学彬 目录: 2013 duilib入门简明教程 -- 前言(1) 2013 duilib入门简明教程 ...
- 空串、null串和isEmpty方法
空串 空串""是长度为0的字符串.可以调用以下代码检查字符串是否为空: if(str.length() == 0) 或 if(str.equals("")) 空 ...
- 洛谷:P3281 [SCOI2013]数数 (优秀的解法)
刷了这么久的数位 dp ,照样被这题虐,还从早上虐到晚上,对自己无语...(机房里又是只有我一个人,寂寞.) 题目:洛谷P3281 [SCOI2013]数数 题目描述 Fish 是一条生活在海里的鱼, ...
- python操作Excel的库openpyxl
http://openpyxl.readthedocs.io/en/default/tutorial.html 这里先上该库的文档镇文. 1,遇到合并后的单元格信息读取的问题,通过使用cell中off ...
- (常用)configparser,hashlib,hamc模块
configparser模块 #专门解析my.ini这种形式的文件(cnf) import configparser config=configparser.ConfigParser() conf ...
- zabbix批量监控urls的状态码
.添加url监控脚本 [root@node1 usr]# vim /usr/local/zabbix_agents_3.2.0/scripts/web_site_code_status.sh #!/b ...
- Uncaught RangeError: Maximum call stack size exceeded
环境: jquery+bootstrap+bootstrapValidator 问题描述:有个form表单,一点击按钮提交,就会报如题错误.正常应该是去校验表单输入. 解决: 从日志分析来看,报错的起 ...
- layui 常见的表单元素
第一步:引用文件 效果图(日期.文件上传在下面): <form class="layui-form" action=""> <div clas ...
- 玩转EhCache之最简单的缓存框架
二.主要特性 快速: 简单: 多种缓存策略: 缓存数据有两级:内存和磁盘,因此无需担心容量问题: 缓存数据会在虚拟机重启的过程中写入磁盘: 可以通过 RMI.可插入 API 等方式进行分布式缓存: 具 ...