重用执行计划以针对具有不同 WHERE 子句的视图进行查询
Reuse execution plan for query against view with different WHERE clauses
我(由于有点奇怪的原因)最终得到了一个视图,编译需要大约 30 秒,但 运行.
不到 3 秒
这是一个位于大量嵌套视图顶部的视图,每个视图都有许多层的顺序 CTE。基础数据集并没有那么大,我想这就是查询最终 运行 非常快的原因。
如果我们总是阅读整个 table,那会很好 - 第一个查询会很慢,而以后的每个查询都会很好。
不幸的是,访问代码将要用日期 window.
读取它
SELECT * FROM myView WHERE date BETWEEN 'foo' AND 'bar'
第一次 运行 编译执行计划需要 30 秒;第二次是 1-3 秒后 运行s。
有什么办法可以防止重新编译吗?
我认识到这可能会导致最终的执行计划效率不高,因为它针对不同的子句进行了优化,但数据非常统一,所以我不希望它太糟糕,而且 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.
一个可能的答案:非内联函数
执行计划缓存将使用第一次调用时的参数,然后继续使用该计划。
缺点
- 定义包装视图的函数非常冗长:(
- 需要针对视图的每个不同 "type" 查询使用单独的函数(或具有大量参数的复杂函数,其中许多参数为 NULL)。
另一个可能的答案:使用存储过程
您再次获得执行计划缓存。
这次您不必像内联 table 值函数一样进行冗长的 table 定义,但是从 Sproc 检索输出不一定像 SELECT 或函数。
最佳答案 (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
)是 运行 时,它的执行计划也会被存储, 但为了使用查询的文本必须完全匹配(包括空格和大小写以及参数值)。
sp_execute 允许临时查询的查询计划独立于参数值存储,以便它可以跨不同的参数集重用(我认为文本的其余部分仍然必须相同)。也就是说,它是为我在这里得到的确切用例而设计的。
(想法和解释归功于 Andy Thomas 作为 Softwire)
[1] 和其他一些我相信的东西,但这与这里无关。
我(由于有点奇怪的原因)最终得到了一个视图,编译需要大约 30 秒,但 运行.
不到 3 秒这是一个位于大量嵌套视图顶部的视图,每个视图都有许多层的顺序 CTE。基础数据集并没有那么大,我想这就是查询最终 运行 非常快的原因。
如果我们总是阅读整个 table,那会很好 - 第一个查询会很慢,而以后的每个查询都会很好。 不幸的是,访问代码将要用日期 window.
读取它SELECT * FROM myView WHERE date BETWEEN 'foo' AND 'bar'
第一次 运行 编译执行计划需要 30 秒;第二次是 1-3 秒后 运行s。
有什么办法可以防止重新编译吗? 我认识到这可能会导致最终的执行计划效率不高,因为它针对不同的子句进行了优化,但数据非常统一,所以我不希望它太糟糕,而且 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.
一个可能的答案:非内联函数
执行计划缓存将使用第一次调用时的参数,然后继续使用该计划。
缺点 - 定义包装视图的函数非常冗长:( - 需要针对视图的每个不同 "type" 查询使用单独的函数(或具有大量参数的复杂函数,其中许多参数为 NULL)。
另一个可能的答案:使用存储过程
您再次获得执行计划缓存。 这次您不必像内联 table 值函数一样进行冗长的 table 定义,但是从 Sproc 检索输出不一定像 SELECT 或函数。
最佳答案 (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
)是 运行 时,它的执行计划也会被存储, 但为了使用查询的文本必须完全匹配(包括空格和大小写以及参数值)。
sp_execute 允许临时查询的查询计划独立于参数值存储,以便它可以跨不同的参数集重用(我认为文本的其余部分仍然必须相同)。也就是说,它是为我在这里得到的确切用例而设计的。
(想法和解释归功于 Andy Thomas 作为 Softwire)
[1] 和其他一些我相信的东西,但这与这里无关。