SQL Server 2008存储过程 - 优化器认为我的参数可以为空

时间:2012-07-27 21:44:45

标签: sql sql-server-2008 stored-procedures parameters null

优化器似乎对varchar参数的无效能力感到困惑,我不确定我理解为什么。我正在使用SQL Server 2008顺便说一句。要查询的所有列都已编制索引。 TDate列是一个聚簇的分区索引。 FooValue列是索引的,不可为空的列。

示例:

CREATE PROCEDURE dbo.MyExample_sp @SDate DATETIME, @EDate DATETIME, @FooValue VARCHAR(50)
AS
SET NOCOUNT ON

--To avoid parameter spoofing / sniffing
DECLARE @sDate1 DATETIME, @eDate1 DATETIME
SET @sDate1 = @sDate
SET @eDate1 = @eDate

SELECT
    fd.Col1,
    fd.Col2,
    fd.TDate,
    fl.FooValue,
    fd.AccountNum
FROM dbo.FooData fd
INNER JOIN dbo.FooLookup fl
    ON fl.FL_ID = fd.FL_ID
WHERE fd.TDate >= @sDate1
    AND fd.TDate < @eDate1
    AND fl.FooValue = @FooValue

将此作为查询运行按预期工作。所有索引都是搜索,没有欺骗等。通过执行sproc运行此操作需要20倍 - 相同的查询 - 相同的参数。但是,如果我做了以下更改(最后一行),一切都会再次起作用。

CREATE PROCEDURE dbo.MyExample_sp @SDate DATETIME, @EDate DATETIME, @FooValue VARCHAR(50)
AS
SET NOCOUNT ON

--To avoid parameter spoofing / sniffing
DECLARE @sDate1, @eDate1
SET @sDate1 = @sDate
SET @eDate1 = @eDate

SELECT
    fd.Col1,
    fd.Col2,
    fd.TDate,
    fl.FooValue,
    fd.AccountNum
FROM dbo.FooData fd
INNER JOIN dbo.FooLookup fl
    ON fl.FL_ID = fd.FL_ID
WHERE fd.TDate >= @sDate1
    AND fd.TDate < @eDate1
    AND fl.FooValue = ISNULL(@FooValue, 'testthis')

就像优化器对参数是否可为空而感到困惑?此外,向参数添加默认值也没有任何区别。除非我使用= isnull(@parameter,'some constant'),否则sproc仍然需要永远运行

我很高兴我明白这一点。但是,我想了解为什么会发生这种情况,以及是否有更优雅的方法来解决这个问题。

1 个答案:

答案 0 :(得分:2)

Re:Nullable变量

在T-SQL中没有可变为nullable的概念,你可以使用?在c#中将变量定义为可为空。 如果存储过程中有参数,最终用户可以将他或她想要的任何内容传递给存储过程,无论是实数值还是空值。

Re:查询计划

将被缓存的查询计划是在您第一次调用此存储过程时生成的查询计划。因此,如果您在第一次运行它时传入了@FooValue的null,那么它将是针对@FooValue = null进行了优化。

您可以使用OPTIMIZE FOR提示来优化查询以获取其他值:

或者您可以使用WITH RECOMPILE,这将强制在每次运行存储过程时重新生成查询计划。

显然在使用这些类型的提示时需要权衡,所以在使用它们之前一定要了解它们。