使用OPTION(RECOMPILE)与连接表的成本

时间:2018-12-12 10:34:09

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

使用:SQL Server 2016 +

我一直在寻找是否有任何方法可以评估SQL Server重新编译查询的执行计划所花费的时间。我们有几个存储过程,可以根据表2中的可为空的参数从表1中进行选择。如果客户不为null,则返回他们的销售额,否则返回所有销售额。

样本数据:

DROP TABLE IF EXISTS dbo.TestTable1;
DROP TABLE IF EXISTS dbo.TestTable2;

CREATE TABLE dbo.TestTable1 (ID INT NOT NULL PRIMARY KEY CLUSTERED , TextValue NVARCHAR(255) NULL);
CREATE TABLE dbo.TestTable2 (ID INT NOT NULL PRIMARY KEY CLUSTERED , TextValue NVARCHAR(255) NULL);

INSERT INTO TestTable1 (ID, TextValue)
VALUES (1, N'Table 1 - Text 1'),
       (2, N'Table 1 - Text 2'),
       (3, N'Table 1 - Text 3'),
       (4, N'Table 1 - Text 4'),
       (5, N'Table 1 - Text 5'),
       (6, N'Table 1 - Text 6'),
       (7, N'Table 1 - Text 7'),
       (8, N'Table 1 - Text 8'),
       (9, N'Table 1 - Text 9'),
       (10, N'Table 1 - Text 10');

INSERT INTO TestTable2 (ID, TextValue)
VALUES (1, N'Table 2 - Text 1'),
       (2, N'Table 2 - Text 2'),
       (3, N'Table 2 - Text 3'),
       (4, N'Table 2 - Text 4'),
       (5, N'Table 2 - Text 5'),
       (6, N'Table 2 - Text 6'),
       (7, N'Table 2 - Text 7'),
       (8, N'Table 2 - Text 8'),
       (9, N'Table 2 - Text 9'),
       (10, N'Table 2 - Text 10');

这大大简化了,因为我们将有多个可能的条件,链接到多个表。当前,我们正在考虑重新编译查询,以便仅在需要时才进行对辅助表的联接。

DECLARE @LookupValue NVARCHAR(50);

SET @LookupValue = NULL;

SELECT  *
  FROM  dbo.TestTable1 T1
 WHERE  @LookupValue IS NULL
    OR  EXISTS ( SELECT TOP (1) 1 A FROM dbo.TestTable2 T2 WHERE T1.ID = T2.ID AND T2.TextValue = @LookupValue)
OPTION (RECOMPILE)

SET @LookupValue = N'Table 2 - Text 1';

SELECT  *
  FROM  dbo.TestTable1 T1
 WHERE  @LookupValue IS NULL
    OR  EXISTS ( SELECT TOP (1) 1 A FROM dbo.TestTable2 T2 WHERE T1.ID = T2.ID AND T2.TextValue = @LookupValue)
OPTION (RECOMPILE);

从下面的查询计划中可以看到,重新编译表2已从执行中有效删除。

Execution Plan

但是,重新编译会产生一定的成本,我正在寻找这种开销,以便我可以明智地决定要格式化哪种查询。总的来说,我看到重新编译的速度一直都比较快,但是有很多文章指出,这意味着执行计划可能远远不够理想。

我们将不胜感激地收到任何有关衡量这些间接费用的指南,或者在更广泛地实施之前应调查的任何问题。

非常感谢。

2 个答案:

答案 0 :(得分:0)

您可以按要求执行操作,而无需重新编译选项,否则每次都会强制生成新计划。您想做任何事情都不要将分支逻辑放在会打败引擎的where子句中,试图弄清楚如何为这两种无法做到的场景创建合适的计划,而这只会造成性能问题。您想要做的是创建一个分支逻辑,以便有两个不同的查询,每个查询都有自己的执行计划。您可以按照以下步骤执行此操作。

Declare @customerid int
Set @customerid = (select customerid from dbo.table2)

If @customerid is null
BEGIN
Select datadesired from table1
END

ELSE
BEGIN
Select datadesired from table1
INNER JOIN table2 ON PKey = FKey
WHERE customerid = @customerID
END

如果要提取所有数据或一组特定的数据,这应该很棒。如果您像建议的那样变得更复杂,那么动态SQL可能是更好的选择。您是否仍然可以使用此方法为每种情况创建查询?当然可以。但是我可以保证,如果您尝试使用branch where子句逻辑来创建两个完全不同的查询,然后向其提供大量数据,那么您将遇到问题,但是进行两个不同的查询以及两个查询都可以正常工作生活在不同的存储过程中,您找出了要在Web层调用的过程(也是可能的)。

答案 1 :(得分:0)

要了解编译时间,请查看:

https://ericblinn.com/quantifying-compile-time

基本上,在查询之前使用SET STATISTICS TIME ON可以获取有关编译和执行所花时间的控制台消息。

请注意,不是您的问题,担心编译时间可能不是最有效的措施。恕我直言,相当低级的后台功能,而且不可预测。

如果您有两个非常不同的查询模式,则最好创建两个(或多个)不同的存储过程,并根据条件由一个入口进行选通,每个入口都有自己的模式(包含或删除可为空的参数) ),并让优化程序和平地进行工作。

在受到连续事务打击的同时强制执行重新计划和重建计划可能不是最明智的选择。

此外,请查看以下博客:

https://blogs.msdn.microsoft.com/robinlester/2016/08/10/improving-query-performance-with-option-recompile-constant-folding-and-avoiding-parameter-sniffing-issues/

它对OPTION(OPTIMIZE FOR(@string =''))有一些见解,可能有用。

但是,如前所述,我从中得出的结论不是使用重新编译,而是设计了在可能时避免使用它的必要性的数据访问过程。