fanybupt

日出而作,日入而息,凿井而饮,耕田而食,帝力于我何有哉?

导航

学习SQLServer - 使用索引

Posted on 2012-05-04 17:59  fanybupt  阅读(409)  评论(0)    收藏  举报

 

注意事项

一个表如果建有大量索引会影响 INSERT、UPDATE、DELETE 和 MERGE 语句的性能,因为当表中的数据更改时,所有索引都须进行适当的调整。

测试1:

CREATE TABLE [dbo].[TEST](
[ID] char(36) not  NULL,
[Index] [int] NULL,
[Data] [int] NULL
)
DECLARE @i INT
select GETDATE()
SET @i=0
WHILE @i<1000000
BEGIN
    INSERT INTO [TEST] ([ID],[Index],[Data])
    SELECT  cast(NewID() as char(36)), @i, @i
    SET @i=@i+1
END
select GETDATE()
delete from test
ALTER TABLE TEST ADD CONSTRAINT TESTPK PRIMARY KEY (ID);
select GETDATE()
SET @i=0
WHILE @i<1000000
BEGIN
    INSERT INTO [TEST] ([ID],[Index],[Data])
    SELECT cast(NewID() as char(36)), @i, @i
    SET @i=@i+1
END
select GETDATE()

测试结果

可以看到两者大概有6秒左右的差别,这部分差别应该是来源于对主键进行排序。两者的执行计划如下

避免对经常更新的表进行过多的索引,并且索引应保持较窄,就是说,列要尽可能少

使用多个索引可以提高更新少而数据量大的查询的性能。大量索引可以提高不修改数据的查询(例如 SELECT 语句)的性能

这个结果在进行排序的时候对比更为明显

对小表进行索引可能不会产生优化效果,因为查询优化器在遍历用于搜索数据的索引时,花费的时间可能比执行简单的表扫描还长

这种情况准确的说应该是对索引扫描次数加上数据扫描次数大于进行表扫描时,使用索引是不划算的,小表(数据量小)更容易出现这种问题

视图包含聚合、表联接或聚合和联接的组合时,视图的索引可以显著地提升性能

为经常用于查询中的谓词和联接条件的所有列创建非聚集索引

涵盖索引可以提高查询性能,因为符合查询要求的全部数据都存在于索引本身中。也就是说,只需要索引页,而不需要表的数据页或聚集索引来检索所需数据,

如下图, 只查询DJBH时,数据库不需要进行额外的键查找来取得数据,也就不需要对数据页进行访问

将插入或修改尽可能多的行的查询写入单个语句内,而不要使用多个查询更新相同的行。仅使用一个语句,就可以利用优化的索引维护。

对于聚集索引,请保持较短的索引键长度。另外,对唯一列或非空列创建聚集索引可以使聚集索引获益。

在上一篇文章学习SQLServer-索引 我提到,索引包含索引的键值以及一个指针,索引较长将会消耗更多的存储空间,对于多列的索引(例如,如果将索引定义为 LastNameFirstName,则该索引在搜索条件为 WHERE LastName = 'Smith'WHERE LastName = Smith AND FirstName LIKE 'J%' 时将很有用。不过,查询优化器不会将此索引用于基于 FirstName (WHERE FirstName = 'Jane') 而搜索的查询。【有待验证】)

对于聚集索引,保证唯一性是很必要的,【如果聚集索引不是唯一的索引,SQL Server 将添加在内部生成的值(称为唯一值)以使所有重复键唯一。此四字节的值对于用户不可见。仅当需要使聚集键唯一以用于非聚集索引中时,才添加该值。】【如果未使用 UNIQUE 属性创建聚集索引,数据库引擎将向表自动添加一个 4 字节的 uniqueifier 列。必要时,数据库引擎将向行自动添加一个 uniqueifier 值以使每个键唯一。】

不能将 ntext、text、image、varchar(max)、nvarchar(max) 和 varbinary(max) 数据类型的列指定为索引键列。不过,varchar(max)、nvarchar(max)、varbinary(max) 和 xml 数据类型的列可以作为非键索引列参与非聚集索引。

检查列的唯一性。在同一个列组合的唯一索引而不是非唯一索引提供了有关使索引更有用的查询优化器的附加信息(?)

在列中检查数据分布。通常情况下,为包含很少唯一值的列创建索引或在这样的列上执行联接将导致长时间运行的查询。

一个例子:22冶Module表OrgTypeID

考虑对具有定义完善的子集的列(例如,稀疏列、大部分值为 NULL 的列、含各类值的列以及含不同范围的值的列)使用筛选索引。设计良好的筛选索引可以提高查询性能,降低索引维护成本和存储成本。

如果索引包含多个列,则应考虑列的顺序。用于等于 (=)、大于 (>)、小于 (<) 或 BETWEEN 搜索条件的 WHERE 子句或者参与联接的列应该放在最前面。其他列应该基于其非重复级别进行排序,就是说,从最不重复的列到最重复的列。

定义索引时,应该考虑索引键列的数据是按升序还是按降序存储。升序是默认设置.当引用表的查询包含用以指定索引中键列的不同方向的 ORDER BY 子句时,指定键值存储在该索引中的顺序很有用。在这些情况下,索引就无需在查询计划中使用 SORT 运算符。因此,使得查询更有效.

 

查询

查询说明和示例

索引

与特定值完全匹配

搜索与特定值完全匹配的项,其中,查询使用 WHERE 子句指定列项。例如:

SELECT BusinessEntityID, JobTitle FROM HumanResources.Employee WHERE BusinessEntityID = 228;

BusinessEntityID列的非聚集或聚集索引。

与 IN (x,y,z) 列表中的某个值完全匹配

搜索与指定值列表中的某个值完全匹配的项。例如:

SELECT BusinessEntityID, JobTitle FROM HumanResources.Employee WHERE BusinessEntityID IN (288, 30, 15);

BusinessEntityID列的非聚集或聚集索引。

值范围

搜索某个值范围,其中,查询指定的任何项的值在两个值之间。例如:

SELECT ProductModelID, Name FROM Production.ProductModel WHERE ProductModelID BETWEEN 1 and 5;

WHERE ProductModelID >= 1 AND ProductModelID <= 5

ProductModelID列的聚集索引或非聚集索引。

表之间的联接

基于联接谓词,在一个表中搜索与另一个表中的某个行匹配的行。例如:

SELECT a.ProductAssemblyID, b.Name, a.PerAssemblyQty FROM Production.BillOfMaterials AS a JOIN Production.Product AS b  ON a.ProductAssemblyID = b.ProductID WHERE b.ProductID = 900;

ProductIDProductAssemblyID列的聚集索引或非聚集索引。

LIKE 比较

搜索以特定字符串(如 abc%)开头的匹配行。例如:

SELECT CountryRegionCode, Name  FROM Person.CountryRegion  WHERE Name LIKE N'D%'

Name列的非聚集或聚集索引

排序或聚合

需要隐式或显式排序顺序或聚合 (GROUP BY)。例如:

SELECT a.WorkOrderID, b.ProductID, a.OrderQty, a.DueDate FROM Production.WorkOrder AS a JOIN Production.WorkOrderRouting AS b ON a.WorkOrderID = b.WorkOrderID ORDER BY a.WorkOrderID;

排序列或聚合列的非聚集索引或聚集索引。

对于排序列,考虑为列指定 ASC 或 DESC 顺序。

PRIMARY KEY 或 UNIQUE 约束

搜索与插入和更新操作中的新索引键值重复的值,以强制 PRIMARY KEY 和 UNIQUE 约束。例如:

INSERT INTO Production.UnitMeasure (UnitMeasureCode, Name, ModifiedDate) VALUES ('OZ1', 'OuncesTest', GetDate());

约束中定义的列的聚集索引或非聚集索引。

PRIMARY KEY/FOREIGN KEY 关系中的 UPDATE 或 DELETE 操作

在列参与 PRIMARY KEY/FOREIGN KEY 关系(无论带不带 CASCADE 选项)的更新或删除操作中搜索行。

外键列的非聚集索引或聚集索引。

列在选择列表中,但不在谓词中。

包含选择列表中未用于搜索和查找的一列或多列。例如:

SELECT Title, Revision, FileName FROM Production.Document WHERE Title LIKE N'%Maintenance%' AND Revision >= 0';

在 INCLUDE 子句中指定了FileName的非聚集索引。