会OPTION(OPTIMIZE FOR UNKNOWN)
重用缓存而不是每次都重新编译吗?
是的,它会的。
从MSDNOPTION(OPTIMIZE FOR UNKNOWN)
的引用中可以看出和之间有两个主要区别:OPTION(RECOMPILE)
OPTIMIZE FOR UNKNOWN
指示查询优化器在编译和优化查询时使用统计数据而不是所有局部变量的初始值,包括使用强制参数化创建的参数。
RECOMPILE
指示 SQL Server 数据库引擎在查询执行后放弃为查询生成的计划,强制查询优化器在下次执行相同查询时重新编译查询计划。如果不指定RECOMPILE
,数据库引擎会缓存查询计划并重用它们。编译查询计划时,RECOMPILE
查询提示使用查询中任何局部变量的当前值,如果查询在存储过程中,则将当前值传递给任何参数。
因此,两个主要区别是:
- 缓存(或不缓存)查询计划。
通常生成的查询计划会被缓存并重用。OPTIMIZE FOR UNKNOWN
不影响引擎的此功能。RECOMPILE
抑制此功能并告诉引擎放弃计划而不是将其放入缓存中。
- 在计划生成期间使用(或不使用)实际参数值。
通常优化器“嗅探”参数值并在生成计划时使用这些值。OPTIMIZE FOR UNKNOWN
抑制此功能并告诉引擎将所有参数视为其值未知。优化器具有内置规则和启发式方法,如何将可用统计信息用于各种过滤条件。请参阅优化……平庸?更多细节。通常,参数嗅探在第一次运行查询/存储过程时使用,并在第一次运行期间使用参数值。生成的计划被缓存,以后可以重用。
这里要记住的一件不明显的事情是,在这两种情况下(正常没有任何查询提示和有OPTIMIZE FOR UNKNOWN
提示),生成的计划必须是有效的并且为任何可能的参数值产生正确的结果。它适用于在正常/无提示情况下首次运行期间使用的嗅探值;它不是针对这种OPTIMIZE FOR UNKNOWN
情况下的任何特定值量身定制的,但如果以后以任何方式更改参数,它仍然有效。
这很重要,它会阻止优化器执行计划的某些转换和简化。
OPTION(RECOMPILE)
允许优化器在每次运行期间内联参数的实际值,并且优化器使用参数的实际值来生成更好的计划。不必担心生成的计划可能不适用于其他参数值,因为该计划不会被缓存和重用。
这种效果在动态搜索条件查询中最为明显。例如:
SELECT ...
FROM T
WHERE
(@ParamSomeID = 0)
OR
(
@ParamSomeID = -1
AND
T.SomeID NOT IN
(
SELECT OtherTable.SomeID
FROM OtherTable
)
)
OR
(
T.SomeID IN
(
SELECT OtherTable.SomeID
FROM OtherTable
WHERE OtherTable.SomeID = @ParamSomeID
)
)
OPTION(RECOMPILE)
If @ParamSomeID
is0
优化器会将查询视为根本没有任何WHERE
子句。该计划根本不会提及OtherTable
。
如果@ParamSomeID
是-1
,则计划将加入T
使用OtherTable
Left Anti Semi Join 并扫描整体OtherTable
。
如果@ParamSomeID
是 5,则计划将在唯一索引中进行索引查找,OtherTable
并仅读取OtherTable
.
没有OPTION(RECOMPILE)
这种简化和转换就不会发生。
使用的另一个原因OPTION(RECOMPILE)
是当您的数据分布非常倾斜时。例如,您有一个包含 1M 行的表。一列在 990K 行中具有值 0,在 1K 行中具有从 1 到 10 的值。根据过滤器的实际值,在此列上过滤的查询应该有不同的计划。
在上面的两个例子中,OPTIMIZE FOR UNKNOWN
都会产生一个平庸的计划。