检查变量是否为NULL会导致性能

时间:2017-09-06 07:29:44

标签: sql-server sql-server-2014 sql-execution-plan sqlperformance

我有以下问题:

DECLARE @application_number CHAR(8)= '37832904';
SELECT
    la.LEASE_NUMBER AS lease_number,
    la.[LEASE_APPLICATION] AS application_number,
    tnu.[FOLLOWUP_CODE] AS note_type_code -- catch codes not in codes table
FROM [dbo].[lease_applications] la
LEFT JOIN [dbo].tickler_notes_uniq tnu ON tnu.[ACCOUNT_NUMBER] = la.[ACCOUNT_NUMBER]
WHERE la.LEASE_APPLICATION = @application_number
      OR @application_number IS NULL;



SELECT
    la.LEASE_NUMBER AS lease_number,
    la.[LEASE_APPLICATION] AS application_number,
    tnu.[FOLLOWUP_CODE] AS note_type_code -- catch codes not in codes table
FROM [dbo].[lease_applications] la
LEFT JOIN [dbo].tickler_notes_uniq tnu ON tnu.[ACCOUNT_NUMBER] = la.[ACCOUNT_NUMBER]
WHERE la.LEASE_APPLICATION = @application_number; 

这两个查询之间的唯一区别是,如果变量为NULL,我已经添加了对变量的检查。

这些查询的执行计划是: enter image description here

您可以找到图形计划here

所以问题是。为什么计划如此不同?

更新:

可以找到第一个查询的实际执行计划here

OPTION(RECOMPILE)将实际执行计划更改为好计划。然而,其缺点是我的主要目标是使用这些参数创建TVF,然后使用该功能的每个人都应该提供该选项。

值得一提的是,我的主要目标是创建2个参数的TVF。它们中的每一个都可能为null,可能不是,但至少其中一个应该是NOT NULL。这些参数或多或少相等,它们只是两个表中的不同键,无论如何都会产生相同的结果(相同的行数等等)。这就是我想做

之类的事情的原因
WHERE (col1 = @param1 OR @param1 IS NULL) AND (col2 = @param2 OR @param2 IS NULL) AND (@param1 IS NOT NULL or @param2 IS NOT NULL)

所以,基本上我对所有记录都不感兴趣

2 个答案:

答案 0 :(得分:4)

对于两个不同的查询,您有两个不同的计划。 有意义的是,当你在WHERE子句(la.LEASE_APPLICATION = @application_number)上有一个相等的条件(并且有索引)时,你会得到一个index seek:按预期工作!

另一方面,当您将两个条件都写入一个WHERE子句(la.LEASE_APPLICATION = @application_number OR @application_number IS NULL)时,查询优化器已选择进行扫描。

即使提供了参数值且它不为null,正在使用的计划是缓存的计划,它在编译时无法知道参数的实际值。

****如果您有一个存储过程并且使用参数调用它,则会出现这种情况。使用变量执行简单查询时不是这种情况。 正如@sepupic所说,变量值不会被嗅到。****

生成计划以处理这两种情况:当您有参数值时以及没有参数值时。

解决问题的一个方法是使用OPTION(RECOMPILE),因为评论中已经说明了这一点。 另一个选择是将您的查询分开(例如,有两个不同的存储过程,由第三个“包装器”程序调用),以便它们相应地进行优化,每个都在它自己的上面。

我建议你看看Kimberly L. Tripp撰写的这篇文章:Building High Performance Stored Procedures以及Aaron Bertrand的另一篇文章:An Updated "Kitchen Sink" Example。我认为这些是解释这种情景的最佳文章。

两篇文章都解释了这种情况,可能存在的问题)以及可能的解决方案,例如选项(重新编译),动态sql或具有分离的存储过程。

祝你好运!

答案 1 :(得分:3)

您的查询未使用<?php // Create a blank image and add some text $im = imagecreatetruecolor(120, 20); $text_color = imagecolorallocate($im, 233, 14, 91); imagestring($im, 1, 5, 5, 'A Simple Text String', $text_color); // Save the image as 'simpletext.jpg' imagejpeg($im, 'simpletext.jpg'); ?> ,他们使用parameters。在编译批处理时(编译=制定计划),变量不会被嗅探,因为批处理被视为一个整体。因此服务器不知道变量是null还是非null。它必须制定一个适合两种情况的计划。

第一个查询可以根本不过滤任何行,因此选择了扫描。

第二个查询会进行过滤,但该值未知,因此如果您使用SQL Server 2014并且已确定的列不是唯一的,则估算值为C ^ 3/4(C =表基数)

如果使用variable查询选项,情况可能会有所不同。当您将其添加到查询中时,它会在完成表变量的分配后重新编译。在这种情况下,变量值是已知的,您将获得另一个计划。它将是一个基于列统计信息的计划,用于过滤器的已知值