重用执行计划以针对具有不同WHERE子句的视图进行查询

时间:2017-03-28 13:42:16

标签: sql-server performance sql-execution-plan

我(因为有些奇怪的原因)最终得到一个需要约30秒才能编译的视图,但是<运行3秒钟。

它是一个坐落在大量嵌套视图顶部的视图,每个视图都有多层顺序CTE。基础数据集并不是那么大,我想这就是为什么查询最终会很快运行。

如果我们总是阅读整个表格,那就没问题了 - 第一个查询速度很慢,之后的每一次搜索都会很好。 不幸的是,访问代码将要使用日期窗口来读取它。

SELECT * FROM myView WHERE date BETWEEN 'foo' AND 'bar'

第一次运行时,编译Exec计划需要30秒;第二次是1-3秒。

无论如何都要阻止重新编译? 我认识到它可能导致最终的执行计划效率不高,因为它针对不同的条款进行了优化,但数据非常统一,所以我不会期望它太糟糕了,而且30秒的编译时间太痛苦了。

我浏览了these pages等网页。但是没有太多的东西立刻跳出来与我相关,并且那些似乎没有实现我的目标(尽管我很容易错过了一些东西)

<小时/> 修改

STATISTICS TIME ON输出类似的视图,在寒冷时运行约6,或者在预编译时运行约1/2秒(事实上,具体来说,它来自其中一个在嵌套中查看一层。)

SELECT * FROM myView WHERE date_incurred < '2017-02-20'

Run with param = '2017-02-20'

SQL Server parse and compile time: CPU time = 5008 ms, elapsed time = 5184 ms.
SQL Server parse and compile time: CPU time = 0 ms, elapsed time = 0 ms.
(77 row(s) affected)
SQL Server Execution Times: CPU time = 452 ms,  elapsed time = 772 ms.

Run with param = '2017-02-20'

SQL Server parse and compile time: CPU time = 0 ms, elapsed time = 0 ms.
SQL Server parse and compile time: CPU time = 0 ms, elapsed time = 0 ms.
(77 row(s) affected)
SQL Server Execution Times: CPU time = 437 ms,  elapsed time = 582 ms.

Run with param = '2017-02-21'

SQL Server parse and compile time: CPU time = 4618 ms, elapsed time = 4877 ms.
SQL Server parse and compile time: CPU time = 0 ms, elapsed time = 0 ms.
(79 row(s) affected)
SQL Server Execution Times: CPU time = 359 ms,  elapsed time = 643 ms.

Run with param = '2017-02-21'

SQL Server parse and compile time: CPU time = 0 ms, elapsed time = 0 ms.
SQL Server parse and compile time: CPU time = 0 ms, elapsed time = 0 ms.
(79 row(s) affected)
SQL Server Execution Times: CPU time = 483 ms,  elapsed time = 559 ms.

3 个答案:

答案 0 :(得分:0)

一个可能的答案:非内联函数

执行计划缓存将首先使用param作为调用,然后继续使用该计划。

缺点   - 非常详细地定义一个包装视图的函数:(   - 对于视图的每个不同的“类型”查询需要一个单独的函数(或者包含大量参数的复杂函数,其中许多是NULL)。

答案 1 :(得分:0)

另一个可能的答案:使用sproc

再次获得Exec Plan缓存。 这次你不必像内联表值函数那样进行详细的表定义,但从Sproc中检索输出并不像SELECT或FUNCTION那样有效。

答案 2 :(得分:0)

最佳答案(IMO):sp_executesql / sp_execute

DECLARE @query int;  
EXEC sp_executesql
    N'SELECT * FROM vlpl_combined_costs_with_invoices WHERE date_incurred < @dateParam OPTION(OPTIMIZE FOR UNKNOWN)',
    N'@dateParam datetime',
    '2017-09-10'


EXEC sp_prepare @query output,   
    N'@dateParam datetime',  
    N'SELECT * FROM myView WHERE date < @dateParam OPTION(OPTIMIZE FOR UNKNOWN)';
EXEC sp_execute @query, '2017-09-07'
EXEC sp_unprepare @query;

我相信前者(sp_executesql)被认为更好,但我不知道具体原因。

<小时/> 的说明

当生成存储过程的执行计划时,它是根据当时传递的参数生成的,然后同一计划被重用于后续执行同一存储过程,而不管参数值 - 只需要匹配名称[1]。

当运行 ad hoc SQL批处理(即常规硬编码SELECT)时,它的执行计划也会被存储,但为了使用,查询的文本已经存在完全匹配(包括空格AND套管和参数值)。

sp_execute 允许独立查询的查询计划独立于参数值进行存储,以便可以在不同的参数集中重复使用(我认为文本的其余部分仍然必须尽管如此)。即它是为我在这里的确切用例而设计的。

(由于想法和解释而感谢Andy Thomas作为Softwire)

[1]以及我认为的其他一些东西,但这与此无关。