使用 FORCESEEK 表提示

FORCESEEK 表提示强制查询优化器仅使用索引查找操作作为访问查询引用的表或者视图中的数据的路径。 您可使用该表提示覆盖查询优化器选择的默认计划,从而避免因低效的查询计划而导致的性能问题。 例如,如果计划中包含表扫描运算符或者索引扫描运算符,且相应的表导致执行查询期间读取十分频繁,如 STATISTICS IO 输出中所示,则强制索引查找操作可能会获得更好的查询性能。 如果优化器因基数或开销估计不准确而在编译计划时倾向于选择扫描操作,则更是如此。

FORCESEEK 适用于聚集索引查找和非聚集索引查找操作。 可以在 SELECT 语句的 FROM 子句和 UPDATE 或 DELETE 语句的 FROM <table_source> 子句中为任何表或视图指定 FORCESEEK。

注意事项注意

由于 SQL Server 查询优化器通常会为查询选择最优执行计划,因此我们建议,只有在最后迫不得已的情况下才可由资深的开发人员和数据库管理员使用提示。

评估查询计划对 FORCESEEK 的适用性

如果查询计划对表或视图使用表扫描或索引扫描运算符,FORCESEEK 表提示可能有用,但索引查找运算符可能更为高效。 请考虑以下查询和后续执行计划。

USE AdventureWorks2008R2;
GO
SELECT *
FROM Sales.SalesOrderHeader AS h
INNER JOIN Sales.SalesOrderDetail AS d 
    ON h.SalesOrderID = d.SalesOrderID 
WHERE h.TotalDue > 100
AND (d.OrderQty > 5 OR d.LineTotal < 1000.00);
GO

下列执行计划显示查询优化器选择了聚集索引扫描运算符来访问两个表中的数据。

Execution plan with clustered index scan 运算符

如下列查询所示,可通过指定 FORCESEEK 提示强制查询优化器对 Sales.SalesOrderDetail 表执行查找操作。

USE AdventureWorks2008R2;
GO
SELECT *
FROM Sales.SalesOrderHeader AS h
INNER JOIN Sales.SalesOrderDetail AS d WITH (FORCESEEK)
    ON h.SalesOrderID = d.SalesOrderID 
WHERE h.TotalDue > 100
AND (d.OrderQty > 5 OR d.LineTotal < 1000.00);
GO

下列执行计划显示了在查询中使用 FORCESEEK 提示的结果。 聚集索引查找操作被用来访问 Sales.SalesOrderDetail 表中的数据。

Execution plan with clustered index seek 运算符

支持索引并集和交集

FORCESEEK 提示支持索引并集和交集。 此提示使查询优化器更可能使用这些技术。 为了避免延长简单查询的编译时间,通常仅在根据将列的基数和选择性考虑在内的规则时才会选择索引并集和交集。 不过,指定 FORCESEEK 提示后,会跳过此类规则,而始终将这些技术考虑在内。 例如,考虑以下查询:

SELECT * FROM T WITH(FORCESEEK) WHERE T.a = 1 AND T.b = 2;

如果表 T 中的列 a 和列 b 具有单独的非聚集索引,则可选择索引交集计划。 也就是说,此计划包含对列 a 的非聚集索引查找操作和对列 b 的非聚集索引查找操作,并且在对基表执行查找操作之前对所得的索引键集求交集。

下例中选择了一个索引并集计划。 也就是说,计划包含对列 a 的查找操作和对列 b 的查找操作,并且在对基表执行查找操作之前对所得的索引键集求并集。

SELECT * FROM T WITH(FORCESEEK) WHERE T.a = 1 OR T.b = 2;

对使用 LIKE 或 IN 的查询应用 FORCESEEK

当查询将 IN 或 LIKE 用做搜索谓词时,查询优化器规则和不准确的基数估计也会导致优化器执行表扫描或索引扫描操作,而不是索引查找操作。

下面的示例演示将 LIKE 或 IN 用做搜索谓词时,FORCESEEK 提示如何强制查询优化器执行索引查找操作,而不是表扫描操作。 若要查看查询执行计划,请在运行此示例前单击**“包括实际的执行计划”**工具栏按钮。

USE tempdb;
GO
DROP TABLE t;
GO
CREATE TABLE t(i int UNIQUE, j int, vc varchar(100));
CREATE INDEX t_vc ON t(vc);
GO
DECLARE @p1 int, @p2 int, @p3 int, @p4 int, @p5 int;
SELECT * FROM t WHERE i IN (@p1, @p2, @p3, @p4, @p5);
GO
DECLARE @p1 int, @p2 int, @p3 int, @p4 int, @p5 int;
SELECT * FROM t WITH (FORCESEEK) WHERE i IN (@p1, @p2, @p3, @p4, @p5);
GO
SELECT * FROM t WHERE vc LIKE 'Test%';
GO
SELECT * FROM t WITH (FORCESEEK) WHERE vc LIKE 'Test%';
GO
DECLARE @vc varchar(100);
SELECT * FROM t WHERE vc LIKE @vc;
GO
DECLARE @vc varchar(100);
SELECT * FROM t WITH (FORCESEEK) where vc like @vc;
GO

对视图使用 FORCESEEK

指定 FORCESEEK 时有无索引提示均可。 将 FORCESEEK 表提示应用到某一视图或索引视图时,FORCESEEK 提示将递归传播到该视图扩展版本中的所有表。 如果已指定索引提示,则会将其忽略。 如果并非每个基础表都至少包含一个索引,则无法找到计划并返回错误 8622。

对索引视图的引用同时使用 FORCESEEK 和 NOEXPAND 提示时,则使用此索引视图时无需事先将其展开。 FORCESEEK 提示将直接应用到索引视图,这与处理表的方式相同。

如果将 FORCESEEK 提示应用至表引用,则表引用无法参与索引视图匹配。 但是,查询中其他不受 FORCESEEK 提示影响的部分可参与索引视图匹配。 这一点与使用 INDEX 提示时索引视图匹配的行为是相似的。

最佳做法注意事项

以下为建议的最佳做法:

  • 在使用 FORCESEEK 表提示之前,请确保数据库中的统计信息是最新的,且准确无误。

    最新的统计信息有助于优化器实现对不同查询计划的开销的准确估算,并选择高质量的计划。 因此,建议对于每个用户数据库,都将 AUTO_CREATE_STATISTICS 和 AUTO_UPDATE_STATISTICS 设置为 ON(默认值)。 或者,您可以使用 UPDATE STATISTICS 语句手动更新表或视图的统计信息。

  • 评估查询看其是否存在可能导致不准确基数估计或开销估计的项,如果可能则删除这些项。 例如,用参数或文字替换局部变量并限制查询中多语句表值函数和表变量的使用。 有关要查找的其他项的更多信息,请参阅 Microsoft SQL Server 2005 中查询优化器使用的统计信息

  • 除非在必要情况下,否则请不要将 INDEX 提示与 FORCESEEK 一起使用。 也就是说,如果单独使用 FORCESEEK 就可生成满足需要的计划,则同时使用 INDEX 提示会过分限制优化器可做的选择。 而且,在更改表的物理架构以删除 INDEX 提示中指定的索引时,此提示将导致查询失败。 相比之下,只要应用 FORCESEEK 提示的表中至少存在一个可使用的索引,则即使在您更改索引结构时查询仍能编译。

  • 请不要将 INDEX 提示 INDEX (0) 与 FORCESEEK 提示一起使用。 INDEX (0) 强制对基表进行扫描。 在与 FORCESEEK 同时使用时,将无法找到计划并返回错误 8622。

  • 请不要将 USE PLAN 查询提示与 FORCESEEK 提示一起使用。 否则,将忽略 FORCESEEK 提示。