优化存储过程中的未知数

use*_*400 3 sql-server stored-procedures execution-plan

我在网上看到的所有示例都暗示OPTIMIZE FOR UNKNOWN应用于特定查询。

可以OPTIMIZE FOR UNKNOWN应用于整个存储过程(而不仅仅是特定查询)?如果是,语法是什么?

Ava*_*rkx 7

不完全,不。

OPTIMIZE FOR是语句级指令,因此,它只能应用于语句级查询 - 无法将OPTIMIZE FOR指令直接应用于整个存储过程。

但是,有一个可用于存储过程的指令,它可能会产生您正在寻找的结果。首先,让我们考虑一下OPTIMIZE FOR实际执行的操作。

当您使用OPTIMIZE FOR提示时(这就是一天结束时的内容,查询计划提示,与特意建议 a 并没有完全不同HASH JOIN),您正在决定优化器应该做什么,并且可能会出现有很多行李,您可能不想携带。这并不是说“不要这样做”,而只是作为一种警告,以确保您了解沿着这条路走下去的含义。在特定情况下OPTIMIZE FOR UNKNOWN,你告诉优化器为语句构建一个查询计划,该计划将适用于任何传递的参数。当包含在存储过程中时,存储过程的查询计划会保留语句的通用计划。一旦被编译和执行,计划就会存储在计划缓存中,并将被重用于后续执行,即使优化器可能已经能够为不同的参数集生成更好的计划,因为优化器无法重新生成计划。

有时,存储过程被非常频繁地调用,这种技术可以通过让优化器知道它不必担心为不同的参数集生成计划来提高性能,允许它跳过重新编译并使用“足够好”的计划以合理快速地产生结果。通常,它用于避免参数嗅探的更邪恶的影响。

CREATE PROCEDURE dbo.SomeProc
    @Name                       sysname
AS BEGIN
    SELECT  TOP 10 *
    FROM    sys.objects
    WHERE   name = @Name
    OPTION ( OPTIMIZE FOR UNKNOWN );
END;
GO
Run Code Online (Sandbox Code Playgroud)

如果调整过程的需要属于消除参数嗅探的范围,则可以选择另一种技术——如果需要,可以应用于整个存储过程级别RECOMPILE。此选项基本上与 完全相反的方法OPTIMIZE FOR,它不是缓存可以稍后重用的查询计划的通用版本,而是告诉优化器在每次执行过程时继续并生成新的查询计划,从而允许优化器为每个执行生成最佳计划。生成查询计划是 CPU 密集型的并且可能非常昂贵(您通常希望计划缓存中的计划),但是如果存储过程不是特别频繁地执行和/或涉及动态 SQL,则使用RECOMPILE选项可以是一种合法的策略。这在 CPU 比 I/O 更便宜的特定情况下也很有帮助,使处理器更努力地工作以缓解一点磁盘争用。

CREATE PROCEDURE dbo.SomeProc
    @Name                       sysname
WITH RECOMPILE
AS BEGIN
    SELECT  TOP 10 *
    FROM    sys.objects
    WHERE   name = @Name;
END;
GO
Run Code Online (Sandbox Code Playgroud)

您可以OPTIMIZE FOR ( @variable_name = UNKNOWN )在语句级别使用或将参数重新声明为局部变量,然后在整个过程 ( SET @p_Name = @Name;, WHERE name = @p_Name;) 中使用这些参数的老派方法,这两者都指示优化器使用统计密度而不是统计直方图,但通常更好只是让优化器存在并将您的注意力转移到问题经常出现的地方:计划缓存本身。清除给您带来痛苦的特定存储过程的计划缓存通常可以缓解任何直接问题,甚至可以让您有时间正确分析和解决为什么您的存储过程性能不佳,而无需采取OPTION相关措施。

SELECT  ProcName = OBJECT_NAME( st.objectid ),
        PlanHandle = cp.plan_handle
FROM    sys.dm_exec_cached_plans cp
CROSS APPLY sys.dm_exec_sql_text( cp.plan_handle ) st
WHERE   cp.objtype = 'Proc'
    AND st.objectid = OBJECT_ID( 'dbo.SomeProc', 'P' );
GO

DBCC FREEPROCCACHE ( <PlanHandle from above results> );
GO
Run Code Online (Sandbox Code Playgroud)


Tar*_*zer 5

无法OPTIMIZE FOR UNKNOWN在 SQL Server 中的存储过程级别进行设置。您可以使用WITH RECOMPILE,除非您使用的是 SQL Server 2005 或更低版本,否则我不建议您使用它,但它无法实现相同的效果。在我看来,OPTIMIZE FOR UNKNOWN应该避免。花时间弄清楚为什么会遇到参数嗅探。可能有一种方法可以在不解决它的情况下修复它。我更喜欢OPTIMIZE FOR @variable_name但也不得不求助于RECOMPILE查询级别。

这篇文章可能会有所帮助。