存储过程提供的结果集与tsql不同,仅在某些服务器上

E.J*_*nan 9 sql-server

这是我昨天提出的一个问题的后续问题:

你曾经有过SQL Server 2008返回与SQL Server 2000不同的结果集吗?

我最初认为存储过程在sql2000和sql2008上给出了不同的结果,但我已经做了相当多的缩小问题并消除了相当多的代码以使其归结为简单/可重现的问题.总结是,当一个TSQL运行时,它返回一个不同的答案,即同一个代码运行的代码只是TSQL,但只在我的客户服务器上运行,而不是在我的任何一个测试服务器上运行.

当我运行这个TSQL时:

DECLARE @PropertyID int 
DECLARE @PortfolioID    int 
DECLARE @StartDate  datetime 
DECLARE @EndDate    datetime 
DECLARE @AcctMethod tinyint 

SET @PropertyId=3555
--SET @PortfolioId = null
SET @StartDate= '3/1/2010'
SET @EndDate='2/28/2011'
SET @AcctMethod=1

DECLARE    @ErrorMsg    varchar(70)
DECLARE @ExclAcct tinyint

SET NOCOUNT ON
CREATE TABLE #IncomeStatement (
    PropertyID      int,
    GLAccountID     int,
    SubTotalAccountID   int,
    Debits          money,
    Credits         money,
    YTDDebits       money,
    YTDCredits      money,
    PZDebits        money,
    PZCredits       money,
    AccountType     tinyint
)

--Initialize Temporary Table
INSERT INTO #IncomeStatement(PropertyID, GLAccountID, SubTotalAccountID, AccountType, Debits, Credits, YTDDebits, YTDCredits, PZDebits, PZCredits)
SELECT PropertyID, ID, SubTotalAccountID, AccountType, 0, 0, 0, 0, 0, 0
FROM ChartOfAccounts
WHERE (PropertyID = @PropertyID OR @PropertyID Is Null)
    AND (@PortfolioID is null OR PropertyID in (select PropertyID from PortfolioProperty where PortfolioID=@PortfolioID))
    AND (Category > 3 or CashFlowCode <> 0)

--Period Activity
IF @AcctMethod = 1
    SET @ExclAcct = 0
ELSE
    SET @ExclAcct = 1

UPDATE Bal
SET 
    Debits = Debits +  D.TotDebit,
    Credits = Credits +  D.TotCredit
FROM #IncomeStatement Bal
    INNER JOIN (SELECT GLAccountID, Sum(Debit) AS TotDebit, Sum(Credit) AS TotCredit
            FROM GLTransaction GT
            WHERE (GT.PropertyID = @PropertyID OR @PropertyID Is Null)
                AND AccountingMethod <> @ExclAcct 
                AND Posted = 1
                AND TranDate >= @StartDate
                AND TranDate <= @EndDate
            GROUP BY GLAccountID) AS D
        ON BAL.GLAccountID = D.GLAccountID 

select * from #IncomeStatement  where GLAccountID=11153 
drop table    #IncomeStatement
Run Code Online (Sandbox Code Playgroud)

但是,当我将上面的代码转换为这样的存储过程时,我的借记金额为124.27美元:

   CREATE Procedure [dbo].[sp_test]
    @PropertyID int = Null,
    @PortfolioID    int = Null,
    @StartDate  datetime = Null,
    @EndDate    datetime = Null,
    @AcctMethod tinyint = 1

AS


DECLARE    @ErrorMsg    varchar(70)
DECLARE @ExclAcct tinyint

SET NOCOUNT ON
CREATE TABLE #IncomeStatement (
    PropertyID      int,
    GLAccountID     int,
    SubTotalAccountID   int,
    Debits          money,
    Credits         money,
    YTDDebits       money,
    YTDCredits      money,
    PZDebits        money,
    PZCredits       money,
    AccountType     tinyint
)

--Initialize Temporary Table
INSERT INTO #IncomeStatement(PropertyID, GLAccountID, SubTotalAccountID, AccountType, Debits, Credits, YTDDebits, YTDCredits, PZDebits, PZCredits)
SELECT PropertyID, ID, SubTotalAccountID, AccountType, 0, 0, 0, 0, 0, 0
FROM ChartOfAccounts
WHERE (PropertyID = @PropertyID OR @PropertyID Is Null)
    AND (@PortfolioID is null OR PropertyID in (select PropertyID from PortfolioProperty where PortfolioID=@PortfolioID))
    AND (Category > 3 or CashFlowCode <> 0)

--Period Activity
IF @AcctMethod = 1
    SET @ExclAcct = 0
ELSE
    SET @ExclAcct = 1

UPDATE Bal
SET 
    Debits = Debits +  D.TotDebit,
    Credits = Credits +  D.TotCredit
FROM #IncomeStatement Bal
    INNER JOIN (SELECT GLAccountID, Sum(Debit) AS TotDebit, Sum(Credit) AS TotCredit
            FROM GLTransaction GT
            WHERE (GT.PropertyID = @PropertyID OR @PropertyID Is Null)
                AND AccountingMethod <> @ExclAcct 
                AND Posted = 1
                AND TranDate >= @StartDate
                AND TranDate <= @EndDate
            GROUP BY GLAccountID) AS D
        ON BAL.GLAccountID = D.GLAccountID 

select * from #IncomeStatement  where GLAccountID=11153 
drop table    #IncomeStatement
Run Code Online (Sandbox Code Playgroud)

然后执行它loke:

EXEC sp_test @PropertyID=3555, @StartDate='03/01/2010', @EndDate='02/28/2011'
Run Code Online (Sandbox Code Playgroud)

我的借方金额为248.54美元,这是应该的两倍.

我真的很难过.在更古怪的事情是,如果我备份这个数据库,然后将其复制到任何我的WIN2003服务器上运行SQL2000,或者我的WIN2008服务器上运行SQL2008R2,它工作正常在这两种情况下.所以,它似乎是一个导致问题的服务器或数据库设置,但是已经没有东西可以检查 - 希望一组新的眼睛可以指出我遗漏的一些明显的东西.

E.J*_*nan 8

好的,这是我的修复 - 它绝对不能解释原始问题,但这就是我所做的:

每当我遇到"参数嗅探"性能问题时,为了解决我为所有参数声明'local'变量,将这些参数分配给那些变量,然后在proc的其余部分中仅使用局部变量,如下所示:

  ALTER Procedure [dbo].[rptDateIncomeStatementPlusCash]
        @PropertyID int = Null,
        @PortfolioID    int = Null,
        @StartDate  datetime = Null,
        @EndDate    datetime = Null,
        @AcctMethod tinyint = 1
    AS
      DECLARE   @xPropertyID    int 
      DECLARE   @xPortfolioID   int 
      DECLARE   @xStartDate datetime 
      DECLARE   @xEndDate   datetime 
      DECLARE   @xAcctMethod    tinyint 

      SET @xPropertyID= @PropertyId
      SET @xPortfolioId = @PortfolioId
      SET @xStartDate = @StartDate
      SET @xEndDate = @EndDate
      SET @xAcctMethod = @AcctMethod
Run Code Online (Sandbox Code Playgroud)

相似之处在于,当参数嗅探是一个问题时,您可以通过MGMT工作室运行存储过程并获得比将其作为SQL运行更好的性能,并且更改(如上所述)通常会修复它.

在我的情况下,我看到直接TSQL与执行proc之间的区别(尽管它与性能无关),我试了一下 - 并且它已经工作了; 我希望我有一个更好的解释,因为老实说,我觉得可怕的是,当运行几乎相同的代码时,SQL服务器将会出现不一致的结果.

我确实从MS发现了这个关于类似但不同的问题的公告,至少确认在正确的情况下,SQL服务器可能会给你错误的答案,并且这个相关的错误报告包含这个关键短语:

描述:两个会话分别通过更改参数值对程序P进行多次调用.过程P对静态数据执行一次查询,有时使用OPTION(RECOMPILE),有时不使用OPTION(RECOMPILE).

偶尔P给出不正确的结果(对于下面的repro,这通常发生在大约1/2% - 1%的时间). 当P的结果错误时,P返回0或预期行数的两倍.

有人昨天留下了关于参数嗅探的评论作为一种可能性,但无论出于何种原因,他们删除了他们的评论(或答案),所以我不能赞美他们的提示.