• 常规索引设计指南


    经验丰富的数据库管理员能够设计出好的索引集,但是,即使对于不特别复杂的数据库和工作负荷来说,这项任务也十分复杂、耗时和易于出错。了解数据库、查询和数据列的特征可以帮助您设计出最佳索引。

    设计索引时,应考虑以下数据库准则:

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

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

      • 使用多个索引可以提高更新少而数据量大的查询的性能。大量索引可以提高不修改数据的查询(例如 SELECT 语句)的性能,因为查询优化器有更多的索引可供选择,从而可以确定最快的访问方法。

    • 对小表进行索引可能不会产生优化效果,因为查询优化器在遍历用于搜索数据的索引时,花费的时间可能比执行简单的表扫描还长。因此,小表的索引可能从来不用,但仍必须在表中的数据更改时进行维护。

    • 视图包含聚合、表联接或聚合和联接的组合时,视图的索引可以显著地提升性能。若要使查询优化器使用视图,并不一定非要在查询中显式引用该视图。有关详细信息,请参阅设计索引视图

    • 使用数据库引擎优化顾问来分析数据库并生成索引建议。有关详细信息,请参阅了解数据库引擎优化顾问

    设计索引时,应考虑以下查询准则:

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

      重要说明重要提示

      避免添加不必要的列。添加太多索引列可能对磁盘空间和索引维护性能产生负面影响。

    • 涵盖索引可以提高查询性能,因为符合查询要求的全部数据都存在于索引本身中。也就是说,只需要索引页,而不需要表的数据页或聚集索引来检索所需数据,因此,减少了总体磁盘 I/O。例如,对某一表(其中对列 a、列 b 和列 c 创建了组合索引)的列 a 和列 b 的查询,仅仅从该索引本身就可以检索指定数据。

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

    • 评估查询类型以及如何在查询中使用列。例如,在完全匹配查询类型中使用的列就适合用于非聚集索引或聚集索引。有关详细信息,请参阅查询类型和索引

    设计索引时,应考虑以下列准则:

    • 对于聚集索引,请保持较短的索引键长度。另外,对唯一列或非空列创建聚集索引可以使聚集索引获益。有关详细信息,请参阅聚集索引设计指南

    • 不能将 ntext、text、image、varchar(max)、nvarchar(max) 和 varbinary(max) 数据类型的列指定为索引键列。不过,varchar(max)、nvarchar(max)、varbinary(max) 和 xml 数据类型的列可以作为非键索引列参与非聚集索引。有关详细信息,请参阅具有包含列的索引

    • xml 数据类型的列只能在 XML 索引中用作键列。有关详细信息,请参阅 XML 数据类型列的索引

    • 检查列的唯一性。在同一个列组合的唯一索引而不是非唯一索引提供了有关使索引更有用的查询优化器的附加信息。有关详细信息,请参阅唯一索引设计指南

    • 在列中检查数据分布。通常情况下,为包含很少唯一值的列创建索引或在这样的列上执行联接将导致长时间运行的查询。这是数据和查询的基本问题,通常不识别这种情况就无法解决这类问题。例如,如果物理电话簿按姓的字母顺序排序,而城市里所有人的姓都是 Smith 或 Jones,则无法快速找到某个人。有关数据分布的详细信息,请参阅使用统计信息提高查询性能

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

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

      例如,如果将索引定义为 LastName、FirstName,则该索引在搜索条件为 WHERE LastName = 'Smith' 或 WHERE LastName = Smith AND FirstName LIKE 'J%' 时将很有用。不过,查询优化器不会将此索引用于基于 FirstName (WHERE FirstName = 'Jane') 而搜索的查询。

    • 考虑对计算列进行索引。有关详细信息,请参阅为计算列创建索引

    在确定某一索引适合某一查询之后,可以选择最适合具体情况的索引类型。索引包含以下特性:

    • 聚集还是非聚集

    • 唯一还是非唯一

    • 单列还是多列

    • 索引中的列是升序排序还是降序排序

    • 非聚集索引是全表还是经过筛选

    您也可以通过设置选项(例如 FILLFACTOR)自定义索引的初始存储特征以优化其性能或维护。有关详细信息,请参阅设置索引选项。而且,通过使用文件组或分区方案可以确定索引存储位置来优化性能。有关详细信息,请参阅在文件组上放置索引

    筛选索引是一种经过优化的非聚集索引,尤其适用于涵盖从定义完善的数据子集中选择数据的查询。筛选索引使用筛选谓词对表中的部分行进行索引。与全表索引相比,设计良好的筛选索引可以提高查询性能、减少索引维护开销并可降低索引存储开销。

    筛选索引与全表索引相比具有以下优点:

    • 提高了查询性能和计划质量          

      设计良好的筛选索引可以提高查询性能和执行计划质量,因为它比全表非聚集索引小并且具有经过筛选的统计信息。与全表统计信息相比,经过筛选的统计信息更加准确,因为它们只涵盖筛选索引中的行。

    • 减少了索引维护开销          

      仅在数据操作语言 (DML) 语句对索引中的数据产生影响时,才对索引进行维护。与全表非聚集索引相比,筛选索引减少了索引维护开销,因为它更小并且仅在对索引中的数据产生影响时才进行维护。筛选索引的数量可以非常多,特别是在其中包含很少受影响的数据时。同样,如果筛选索引只包含频繁受影响的数据,则索引大小较小时可以减少更新统计信息的开销。

    • 减少了索引存储开销          

      在没必要创建全表索引时,创建筛选索引可以减少非聚集索引的磁盘存储开销。可以使用多个筛选索引替换一个全表非聚集索引而不会明显增加存储需要。

    为了设计有效的筛选索引,必须了解应用程序使用哪些查询以及这些查询与您的数据子集有何关联。例如,所含值中大部分为 NULL 的列、含异类类别的值的列以及含不同范围的值的列都属于具有定义完善的子集的数据。以下设计注意事项提供了筛选索引优于全表索引的各种情况。

    数据子集的筛选索引

    在列中只有少量相关值需要查询时,可以针对值的子集创建筛选索引。例如,当列中的值大部分为 NULL 并且查询只从非 NULL 值中进行选择时,可以为非 NULL 数据行创建筛选索引。由此得到的索引与对相同键列定义的全表非聚集索引相比,前者更小且维护开销更低。

    例如,AdventureWorks2008R2 数据库中有一个包含 2679 行的 Production.BillOfMaterials 表。EndDate 列只有 199 行包含非 NULL 值,其余 2480 行均包含 NULL。下面的筛选索引将涵盖这样的查询:返回在此索引中定义的列的查询,以及只选择 EndDate 值不为 NULL 的行的查询。

     
    USE AdventureWorks2008R2;
    GO
    IF EXISTS (SELECT name FROM sys.indexes
        WHERE name = N'FIBillOfMaterialsWithEndDate'
        AND object_id = OBJECT_ID (N'Production.BillOfMaterials'))
    DROP INDEX FIBillOfMaterialsWithEndDate
        ON Production.BillOfMaterials
    GO
    CREATE NONCLUSTERED INDEX FIBillOfMaterialsWithEndDate
        ON Production.BillOfMaterials (ComponentID, StartDate)
        WHERE EndDate IS NOT NULL ;
    GO
    

    筛选索引 FIBillOfMaterialsWithEndDate 对下面的查询有效。您可以显示查询执行计划,以确定查询优化器是否使用了此筛选索引。有关如何显示查询执行计划的信息,请参阅分析查询

     
    SELECT ProductAssemblyID, ComponentID, StartDate 
    FROM Production.BillOfMaterials
    WHERE EndDate IS NOT NULL 
        AND ComponentID = 5 
        AND StartDate > '01/01/2008' ;
    GO
    

    有关如何创建筛选索引以及如何定义筛选索引谓词表达式的详细信息,请参阅 CREATE INDEX (Transact-SQL)

    异类数据的筛选索引

    表中含有异类数据行时,可以为一种或多种类别的数据创建筛选索引。

    例如,Production.Product 表中列出的每种产品均分配到一个 ProductSubcategoryID,后者又与 Bikes、Components、Clothing 或 Accessories 产品类别关联。这些类别为异类类别,因为它们在 Production.Product 表中的列值并不是紧密相关的。例如,对于每种产品类别,Color、ReorderPoint、ListPrice、Weight、Class 和 Style 均具有唯一特征。假设会经常查询具有子类别 27-36 的 Accessories。通过对 Accessories 子类别创建筛选索引,可以提高对 Accessories 的查询的性能。

    下面的示例对 Production.Product 表中 Accessories 子类别中的所有产品创建一个筛选索引。

     
    USE AdventureWorks2008R2;
    GO
    IF EXISTS (SELECT name FROM sys.indexes
        WHERE name = N'FIProductAccessories'
        AND object_id = OBJECT_ID ('Production.Product'))
    DROP INDEX FIProductAccessories
        ON Production.Product;
    GO
    CREATE NONCLUSTERED INDEX FIProductAccessories
        ON Production.Product (ProductSubcategoryID, ListPrice) 
            Include (Name)
    WHERE ProductSubcategoryID >= 27 AND ProductSubcategoryID <= 36;
    GO
    
    

    筛选索引 FIProductAccessories 涵盖下面的查询,因为查询

    结果包含在该索引中,并且查询计划不包括基表查找。例如,查询谓词表达式 ProductSubcategoryID = 33 是筛选索引谓词 ProductSubcategoryID >= 27 和 ProductSubcategoryID <= 36 的子集,查询谓词中的 ProductSubcategoryID 和 ListPrice 列全都是索引中的键列,并且名称作为包含列存储在索引的叶级别。

     
    SELECT Name, ProductSubcategoryID, ListPrice
    FROM Production.Product
    WHERE ProductSubcategoryID = 33 AND ListPrice > 25.00 ;
    GO
    
    

    视图与筛选索引

    视图是存储查询定义的虚拟表;与筛选索引相比,其用途更广,功能更强。有关视图的详细信息,请参阅了解视图使用视图的情况。下表比较了在视图和筛选索引中可以使用的部分功能。

     

    在表达式中允许

    视图

    筛选的索引

    计算列

    联接

    多个表

    谓词中的简单比较逻辑*

    谓词中的复杂逻辑**

    *有关谓词中的简单比较逻辑,请参阅 CREATE INDEX 中的 WHERE 子句语法。

    **有关谓词中的复杂比较逻辑,请参阅 SELECT 中的 WHERE 子句语法。

    不能对视图创建筛选索引。但是,查询优化器可以从对视图中引用的表定义的筛选索引中获益。对于从视图中选择数据的查询,如果查询结果正确,查询优化器会考虑对此查询使用筛选索引。下面的示例创建一个视图(开始日期在 2000 年 4 月 1 日以后)和一个筛选索引(开始日期在 2000 年 8 月 1 日以后)。

    USE AdventureWorks2008R2;
    GO
    IF OBJECT_ID ('ViewOnBillOfMaterials') IS NOT NULL
    DROP VIEW ViewOnBillOfMaterials;
    GO
    CREATE VIEW ViewOnBillOfMaterials AS 
    SELECT ComponentID, StartDate, EndDate, StartDate + 2 AS ShipDate
    FROM Production.BillOfMaterials
    WHERE StartDate > '20000401';
    GO
    IF EXISTS (SELECT name FROM sys.indexes
        WHERE name = N'FIBillOfMaterialsByStartDate'
        AND object_ID = OBJECT_ID (N'Production.BillOfMaterials'))
    DROP INDEX FIBillOfMaterialsByStartDate 
        ON Production.BillOfMaterials;
    GO
    CREATE NONCLUSTERED INDEX FIBillOfMaterialsByStartDate
        ON Production.BillOfMaterials (ComponentID, StartDate, EndDate)
    WHERE StartDate > '20000801';
    GO
    
    
    

    在下面的示例中,查询选择 2004 年 9 月 1 日以后的开始日期,这些日期完全包含在此筛选索引和筛选视图中。查询优化器将考虑使用筛选索引 FIBillOfMaterialsByStartDate,因为其中包含了正确的查询结果。

     
    SELECT StartDate, ComponentID FROM ViewOnBillOfMaterials
    WHERE StartDate > '20040901';
    GO
    

    在下一示例中,查询选择 2004 年 6 月 1 日以后的开始日期,这些日期完全包含在此视图中,但未完全包含在此筛选索引中。查询优化器不考虑使用筛选索引 FIBillOfMaterialsByStartDate,因为与查询从视图中选择数据所返回的正确结果相比,查询使用筛选索引会返回不同的结果。

     
    SELECT StartDate, ComponentID FROM ViewOnBillOfMaterials
    WHERE StartDate > '20040601';
    GO
    

    索引视图与筛选的索引的对比

    筛选的索引与索引视图相比,具有以下优点:

    • 减少了索引维护开销。例如,相对于索引视图而言,查询处理器使用更少的 CPU 资源便可更新筛选的索引。

    • 改善了计划质量。例如,在查询编译期间,查询优化器考虑使用筛选的索引的情况要比考虑使用等效的索引视图的情况多。

    • 联机索引重新生成。您可以在筛选的索引可用于查询时重新生成它们。索引视图不支持联机索引重新生成。有关详细信息,请参阅 ALTER INDEX (Transact-SQL) 的 REBUILD 选项。

    • 非唯一索引。筛选的索引可以是非唯一的,而索引视图必须是唯一的。

    出于以上原因,建议尽可能使用筛选的索引,不使用索引视图。如果满足以下条件,则可以使用筛选的索引而不使用索引视图:视图只引用一个表,查询不返回计算列且视图谓词使用简单的比较逻辑。例如,允许在视图定义中使用如下谓词表达式,但不允许在筛选索引中使用它,因为它包含 LIKE 运算符。

    WHERE StartDate > '20040701' AND ModifiedDate LIKE 'E%'            

    键列

    最好在筛选索引定义中包含少量的键或包含列,并且只包含查询优化器为查询执行计划选择筛选索引所需的列。无论某一筛选索引是否涵盖了查询,查询优化器都可以为查询选择此筛选索引。但是,如果某一筛选索引涵盖了查询,则查询优化器更有可能选择此筛选索引。有关涵盖查询的详细信息,请参阅创建带有包含列的索引

    在某些情况下,筛选索引涵盖查询,但没有将筛选索引表达式中的列作为键或包含列包括在筛选索引定义中。以下准则说明了筛选索引表达式中的列何时应为筛选索引定义中的键或包含列。这些示例引用了此前创建的筛选索引 FIBillOfMaterialsWithEndDate。

    如果筛选索引表达式等效于查询谓词并且查询并未在查询结果中返回筛选索引表达式中的列,则筛选索引表达式中的列不需要作为筛选索引定义中的键或包含列。例如,FIBillOfMaterialsWithEndDate 涵盖下面的查询,因为查询谓词等效于筛选表达式,并且查询结果中未返回 EndDate。FIBillOfMaterialsWithEndDate 不需要将 EndDate 作为筛选索引定义中的键或包含列。

     
    SELECT ComponentID, StartDate FROM Production.BillOfMaterials
    WHERE EndDate IS NOT NULL;
    GO
    

    如果查询谓词在不与筛选索引表达式等效的比较中使用了筛选索引表达式中的某列,则该列应为筛选索引定义中的键或包含列。例如,FIBillOfMaterialsWithEndDate 对下面的查询有效,因为它从筛选索引中选择了行的子集。但是,它不涵盖下面的查询,因为在比较 EndDate > '20040101' 中使用了 EndDate,此比较不与筛选索引表达式等效。查询处理器在不查找 EndDate 值的情况下无法执行此查询。因此,EndDate 应为筛选索引定义中的键或包含列。

     
    SELECT ComponentID, StartDate FROM Production.BillOfMaterials
    WHERE EndDate > '20040101';
    GO
    

    如果筛选索引表达式中的某列在查询结果集中,则该列应为筛选索引定义中的键或包含列。例如,FIBillOfMaterialsWithEndDate 不涵盖下面的查询,因为它在查询结果中返回了 EndDate 列。因此,EndDate 应为筛选索引定义中的键或包含列。

     
    SELECT ComponentID, StartDate, EndDate FROM Production.BillOfMaterials
    WHERE EndDate IS NOT NULL;
    GO
    

    表的主键不需要是筛选索引定义中的键或包含列。主键自动包含在所有非聚集索引(包括筛选索引)中。

    筛选谓词中的数据转换运算符

    如果筛选索引结果的筛选索引表达式中指定的比较运算符会导致隐式或显式数据转换,则转换发生在比较运算符的左边时,会出现错误。解决方法是在比较运算符的右边编写包含数据转换运算符(CAST 或 CONVERT)的筛选索引表达式。

    下面的示例创建一个包含多种数据类型的表。

    USE AdventureWorks2008R2;
    GO
    IF OBJECT_ID ('dbo.TestTable') IS NOT NULL
    DROP TABLE dbo.TestTable;
    GO
    CREATE TABLE TestTable (a int, b varbinary(4));
    GO
    
    
    

    在下面的筛选索引定义中,列 b 隐式转换为整数数据类型,以便与常量 1 进行比较。因为转换发生在筛选谓词中运算符的左边,所以这会生成错误消息 10611。

     
    USE AdventureWorks2008R2;
    GO
    IF EXISTS ( SELECT name from sys.indexes 
        WHERE name = N'TestTabIndex'
        AND object_id = OBJECT_ID (N'dbo.TestTable'))
    DROP INDEX TestTabIndex on dbo.TestTable
    GO
    CREATE NONCLUSTERED INDEX TestTabIndex ON dbo.TestTable(a,b)
    WHERE b = 1;
    GO
    

    解决方法是将右侧的常量转换为与列 b 的类型相同的类型,如下例所示:

     
    CREATE INDEX TestTabIndex ON dbo.TestTable(a,b)
    WHERE b = CONVERT(Varbinary(4), 1);
    GO
    

    将数据转换从比较运算符的左边移动到右边可能会改变转换的含义。在上例中,将 CONVERT 运算符添加到右边时,相应的比较从整数比较更改为 varbinary 比较。

    引用依赖项

    sys.sql_expression_dependencies 目录视图将筛选索引表达式中的每一列作为一个引用依赖项进行跟踪。不能删除、重命名或更改在筛选索引表达式中定义的表列的定义。

    何时使用筛选索引

    列中包含查询在 SELECT 语句中引用的定义完善的数据子集时,筛选索引很有用。以下是一些示例:

    • 仅包含少量非 NULL 值的稀疏列。

    • 包含多种类别的数据的异类列。

    • 包含多个范围的值(如美元金额、时间和日期)的列。

    • 由列值的简单比较逻辑定义的表分区。

    如果索引中的行数与全表索引相比较少时,筛选索引减少的维护开销最为明显。如果筛选索引包含表中的大部分行,则与全表索引相比,其维护开销可能更高。在这种情况下,应使用全表索引而不是筛选索引。

    筛选索引是针对一个表定义的,仅支持简单比较运算符。如果需要引用多个表或具有复杂逻辑的筛选表达式,则应创建视图。

    筛选索引功能支持

    一般情况下,数据库引擎和工具为筛选索引提供了与非聚集全表索引相同的支持,将筛选索引视为特殊类型的非聚集索引。下面的列表提供了有关对筛选索引提供完全支持、不提供支持或提供有限支持的工具和功能的说明。

    • ALTER INDEX 支持筛选索引。若要修改筛选索引表达式,请使用 CREATE INDEX WITH DROP_EXISTING。

    • 缺失索引功能不建议使用筛选索引。

    •                   数据库引擎优化顾问在提供索引优化建议时会考虑筛选索引,并且可能会建议 is not null 筛选索引。

    • 联机索引操作支持筛选索引。

    • 表提示支持筛选索引,但有一些不适用于非筛选索引的限制。下一节将介绍这些内容。

    如果不论是否使用筛选索引,查询均选择相同的结果,则查询优化器会使用筛选索引。此前介绍的筛选索引 FIBillOfMaterialsWithEndDate 对以下两个查询有效。在第一个示例中,查询谓词与筛选索引谓词 WHERE EndDate IS NOT NULL 完全匹配。在第二个示例中,由于查询谓词包含索引中行的子集,所以它比筛选谓词具有更强的选择性。

     
    SELECT ComponentID, StartDate FROM Production.BillOfMaterials
    WHERE EndDate IS NOT NULL;
    GO
    SELECT ComponentID, StartDate FROM Production.BillOfMaterials
    WHERE EndDate < '20040701';
    GO
    

    下一个查询也可使用 FIBillOfMaterialsWithEndDate。但是,由于存在其他决定查询开销的因素(如查询谓词的选择性),优化器可能不会选择筛选索引。如下例所示,可以通过将筛选索引用作查询提示强制优化器选择筛选索引。

     
    SELECT ComponentID, StartDate FROM Production.BillOfMaterials
        WITH ( INDEX ( FIBillOfMaterialsWithEndDate ) )
    WHERE EndDate IN ('20040825', '20040908', '20040918');
    GO
    

    如果查询可以返回不在筛选索引中的行,则查询优化器将不会使用筛选索引。例如,查询优化器将不考虑对下面的查询使用 FIBillOfMaterialsWithEndDate,因为查询可能返回 NULL EndDate 值和非 NULL ModifiedDate 值,这些值不能包含在 FIBillOfMaterialsWithEndDate 中(因为它只包含非 NULL EndDate 值)。

     
    SELECT ComponentID, StartDate FROM Production.BillOfMaterials
    WHERE EndDate IS NOT NULL OR ModifiedDate IS NOT NULL;
    GO
    

    如果筛选索引显式用作表提示且可能未包含所有查询结果,则查询优化器产生查询编译错误 8622。在下面的示例中,查询优化器产生错误 8622,因为 FIBillOfMaterialsWithEndDate 对于查询无效且它显式用作索引提示:

     
    SELECT StartDate, ComponentID FROM Production.BillOfMaterials
        WITH ( INDEX ( FIBillOfMaterialsWithEndDate ) )
    WHERE EndDate IS NOT NULL OR ModifiedDate IS NOT NULL;
    GO
    

    参数化查询

    在某些情况下,参数化查询在编译时包含的信息不足以满足查询优化器选择筛选索引的需要。可能可以重写此查询以提供缺少的信息。在下面的示例中,查询优化器不考虑对 SELECT 语句使用筛选索引 FIBillOfMaterialsWithComponentID,因为 @p 和 @q 的参数值在编译时未知。下面的查询示例运行时将 SHOWPLAN_XML 设置为 ON,以使您可以在 SHOWPLAN_XML 输出中查看参数化查询的不匹配的筛选索引。

    USE AdventureWorks2008R2;
    GO
    IF EXISTS ( SELECT name FROM sys.indexes
        WHERE name = N'FIBillOfMaterialsWithComponentID'
        AND object_id = OBJECT_ID (N'Production.BillOfMaterials'))
    DROP INDEX FIBillOfMaterialsWithComponentID
        ON Production.BillOfMaterials;
    GO
    CREATE NONCLUSTERED INDEX FIBillOfMaterialsWithComponentID
        ON Production.BillOfMaterials (ComponentID, StartDate, EndDate)
    WHERE ComponentID IN (533, 324, 753);
    GO
    SET SHOWPLAN_XML ON;
    GO
    DECLARE @p AS INT, @q AS INT;
    SET @p = 533;
    SET @q = 324;
    SELECT StartDate, ComponentID from Production.BillOfMaterials 
    WHERE ComponentID = @p OR ComponentID = @q;
    GO
    SET SHOWPLAN_XML OFF;
    GO
    
    
    

    SHOWPLAN_XML 输出中的 UnmatchedIndexes 元素和 Parameterization 子元素指示筛选索引与查询不匹配。有关如何查看 SHOWPLAN_XML 输出的信息,请参阅 XML 显示计划

    解决方法是修改此查询,使在参数化表达式不是筛选谓词的子集时查询结果为空。下面的查询说明了如何进行这种修改。通过将 ComponentID in (533, 324, 753) 表达式添加到 WHERE 子句,确保了此查询的结果是筛选谓词表达式的子集。通过这种修改,查询优化器可以考虑对下面的 SELECT 语句使用筛选索引 FIBillOfMaterialsWithComponentID。

     
    USE AdventureWorks2008R2;
    GO
    SET SHOWPLAN_XML ON;
    GO
    DECLARE @p AS INT, @q AS INT;
    SET @p = 533;
    SET @q = 324;
    SELECT StartDate, ComponentID FROM Production.BillOfMaterials
    WHERE ComponentID in (533, 324, 753)
        AND (ComponentID = @p OR ComponentID = @q);
    GO
    SET SHOWPLAN_XML OFF;
    GO
    

    简单参数化

    在大多数情况下,如果某查询计划包括筛选索引,查询优化器将不对该查询执行简单参数化(在 SQL Server 2005 中称为“自动参数化”)。对此类查询执行简单参数化可扩大可能参数值的范围,这样,筛选索引便不能保证查询结果的准确性。例如,如果 SELECT 语句的 WHERE 子句使用了在筛选索引的谓词中使用的列,则查询优化器可能不会执行简单参数化,这是因为查询计划中很可能会包括筛选索引。

    如果适合,使用本节中所述的准则重写查询以确保筛选索引将涵盖该查询,也许能够参数化该查询。

    使用键查找的查询

    查询优化器可以执行键查找操作来检索筛选索引没有涵盖的剩余列,从而即使在某筛选索引不涵盖查询的情况下也可以使用该筛选索引。有关键查找的详细信息,请参阅Key Lookup Showplan 运算符。如果估计的键查找次数很少,则查询优化器可能会选择此方法。下面的查询使用索引提示强制查询处理器使用 FIBillOfMaterialsWithEndDate,同时对 EndDate 执行书签查找操作。对于查询谓词中的 EndDate > @date 比较,会执行键查找操作。

     
    USE AdventureWorks2008R2;
    GO
    DECLARE @date AS DATE;
    SET @date = '20040825'
    SELECT ComponentID, StartDate, EndDate FROM Production.BillOfMaterials
    WITH ( INDEX (FIBillOfMaterialsWithEndDate) )
    WHERE EndDate > @date;
    GO
    

    请注意,EndDate > @Date 与筛选索引表达式 EndDate IS NOT NULL 不完全匹配。筛选索引对此参数化查询仍有效,因为它返回了由筛选索引表达式定义的行的子集。

  • 相关阅读:
    MyISAM和InnoDB的区别
    MySQL——索引与优化
    jquery选择器及效率问题
    Mac 可设置环境变量的位置、查看和添加PATH环境变量
    javascript默认中文(汉字/标点)长度均为1的解决
    苹果下抓屏截屏方法 包括全屏、选择区域、窗口抓屏等
    java实现window phone推送通知
    设计模式总结
    NHibernate 帮助类(单例实际运用)
    访问者模式
  • 原文地址:https://www.cnblogs.com/firstdream/p/8807942.html
Copyright © 2020-2023  润新知