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

时间:2011-04-26 20:03:35

标签: sql-server

这是我昨天问的一个问题的后续跟进:

Have you ever had SQL Server 2008 return a different result set than SQL Server 2000?

我最初认为存储过程在sql2000和sql2008上给出了不同的结果,但我已经做了相当多的缩小问题并消除了相当多的代码以使其归结为简单/可重现的问题。总结是,作为一个proc运行时,一段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

当我将上述代码转换为这样的存储过程时,我获得了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

然后执行它:

EXEC sp_test @PropertyID=3555, @StartDate='03/01/2010', @EndDate='02/28/2011'

我得到的借记金额为248.54美元,这应该是应该的两倍。

我真的很难过。更奇怪的是,如果我备份这个数据库,然后将其复制到运行sql2000的win2003服务器或运行SQL2008R2的win2008服务器,它在两种情况下都能正常工作。所以,似乎它是一个导致问题的服务器或数据库设置,但已经用完了要检查的东西 - 希望一组新的眼睛可以指出我遗漏的明显事物。

2 个答案:

答案 0 :(得分: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

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

在我的情况下,我发现直接TSQL与执行proc之间存在差异(尽管它与性能无关),我试了一下 - 并且预先确定它有效;我希望我有一个更好的解释,因为老实说,我发现在运行几乎相同的代码时,认为SQL服务器会出现不一致的结果是可怕的。

我确实找到this bulletin from MS关于类似但不同的问题,至少确认在正确的情况下,SQL服务器可能会给你不好的答案,并this related bug report使用这个关键短语:

  

描述:每个会话两个   很多人打电话给P程序   改变参数值。该   过程P执行一个查询   静态数据,有时使用OPTION   (RECOMPILE),有时没有。

     

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

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

答案 1 :(得分:1)

Here is the execution plan, for those interested - could figure out how do this as a comment