使用OPTION(RECOMPILE)与加入表格的成本

问题描述 投票:1回答:2

使用: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

然而,重新编译会有成本,我正在寻找这个开销,因此我可以做出明智的决定,以便以这种方式格式化我们的哪些查询。总的来说,我看到重新编译的速度一直很快,但有很多帖子说这意味着执行计划可能会远远低于最优。

我们将非常感激地收到关于衡量这些间接费用的任何指导或我应该在更广泛地实施之前调查的任何问题。

非常感谢。

sql sql-server optimization sql-execution-plan
2个回答
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可能是更好的选择。您仍然可以使用此方法为每个方案创建查询吗?当然,如果你想。但我几乎可以保证,如果你尝试使用分支where子句逻辑来创建两个完全不同的查询,然后给它任何大量的数据,你将遇到问题,但是如果这两个查询同时进行两个不同的查询住在不同的存储过程中,你想出了在Web层调用哪个过程(也是可能的)。


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 =''))有一些可能有用的见解。

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

© www.soinside.com 2019 - 2024. All rights reserved.