3

优化器似乎对 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 仍然需要永远运行

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

4

1 回答 1

2

回复:可为空的变量

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

回复:查询计划

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

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

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

显然,在使用这些类型的提示时需要权衡取舍,因此请确保在使用它们之前了解它们。

于 2012-09-06T19:38:04.373 回答