【问题标题】:"Could not complete cursor operation because the set options have changed since the cursor was declared" error“无法完成游标操作,因为自声明游标后设置选项已更改”错误
【发布时间】:2014-02-13 05:06:09
【问题描述】:

我们在 SqlServer 数据库上有以下存储过程。它适用于早期版本。但它不适用于最新版本(SqlServer Express 2012)。该过程编译良好。但是运行时会抛出错误信息。

我对在 SqlServer 上编程存储过程几乎一无所知。这是 10 多年前创建的程序,并且一直有效。 (但现在没有)。

任何想法我需要更改以纠正它?

 CREATE PROCEDURE [GETBOUND] @mTableName char(20), @FIELD_NAME CHAR(20), @FIELD_VALUE CHAR(50), @DIRECTION CHAR(1), @nRec int , @ADD_COND CHAR(100) AS
  declare @myresult char(50)
  declare @counter numeric
  declare @mDesc char(5)
  declare @mEval char(2)


  if @Direction = 'L' BEGIN
    SELECT @mDesc = ' DESC'
    SELECT @mEval = '<='
  END ELSE BEGIN
    SELECT @mDesc = ''
    SELECT @mEval = '>='
  END


  exec('DECLARE THESQL   CURSOR FOR SELECT '+@field_name+' FROM '+@mTableName+'  WHERE '+@ADD_COND+@field_name+@mEval+''''+ @Field_Value+''' order by '+@FIELD_NAME+@mDesc)
  SET ROWCOUNT @nrec

   open THESQL


     fetch THESQL into @myresult

  select @COUNTER = 1

  WHILE (@COUNTER <@nRec)
  BEGIN
     fetch THESQL into @myresult
      SELECT @COUNTER = @COUNTER +1
  END


   close THESQL
  deallocate THESQL
 select @myresult

【问题讨论】:

  • SET ROWCOUNT 现在是 SQL Server 2012 中的计划缓存键。当0 为非零时(set_options 中的262144),它会编译不同的计划
  • 如此快速的解决方法是将SET ROWCOUNTexec('DECLARE THESQL 的顺序颠倒过来。虽然实际上我认为这对你没有任何帮助。不过,您可能应该使用嵌套的 TOPROW_NUMBER 从头开始​​重写整个内容。
  • 谢谢马丁。反转这些使它起作用。请把它作为一个答案,这样我就可以给你信用。

标签: sql-server sql-server-2012


【解决方案1】:

我冒昧地重写了您的存储过程。你根本不需要CURSOR 最后你只需从光标返回最后一个元素。为此,我只是颠倒了逻辑,所以现在它将返回 TOP 1 记录而没有光标。

此外,我重命名了一些变量,将它们设为 varchar 而不是 char,并让它们变得更大。我的大多数桌子都超过 20 个字符。

我已删除不再使用的变量。

EXEC [ups_GetBound]
    @pTable_Name = 'tables'
   ,@pField_Name = 'name'
   ,@pField_Value = 'MyTable'
   ,@pDirection = 'M'
   ,@pExtra_Condition = ''
   ,@pCounter = 13;


CREATE PROC [ups_GetBound]
    (
     @pTable_Name SYSNAME
    ,@pField_Name SYSNAME
    ,@pField_Value VARCHAR(50)
    ,@pDirection CHAR(1)
    ,@pExtra_Condition VARCHAR(100)
    ,@pCounter INT 
    )
AS
    BEGIN
        DECLARE @mDesc CHAR(5)
           ,@mEval CHAR(2)
           ,@Extra_Condition NVARCHAR(100) = '';

        IF @pDirection = 'L'
            BEGIN
                SELECT @mDesc = ' DESC'
                SELECT @mEval = '<='
            END
        ELSE
            BEGIN
                SELECT @mDesc = ' '
                SELECT @mEval = '>='
            END

        DECLARE @sql NVARCHAR(500)
           ,@ParamDefenition NVARCHAR(250);

        SET @ParamDefenition = N'@pField_Value VARCHAR(50),@pCounter INT'

        SET @sql = N'WITH CounterSelect as (SELECT ' + QUOTENAME(@pField_Name) 
            + ', ROW_NUMBER() OVER (ORDER BY '+ QUOTENAME(@pField_Name) + @mDesc + ') as RowNum' 
            + ' FROM ' + QUOTENAME(@pTable_Name) 
            + ' WHERE '+ @pExtra_Condition + QUOTENAME(@pField_Name) + @mEval + '@pField_Value )' 
            + ' SELECT ' + QUOTENAME(@pField_Name) 
            + ' From CounterSelect WHERE RowNum = @pCounter'

        --PRINT @sql
        EXEC sp_executesql
            @sql
           ,@ParamDefenition
           ,@pField_Value
           ,@pCounter
    END

【讨论】:

  • 你确定这和原来的语义一样吗?我没有花太长时间看它,但假设它应该通过简单地获取每一行并丢弃结果的效率极低的方法找到第 n 行(其中 n = @nRec)。应该使用QUOTENAME 来避免SQL 注入。 sysname 是表名和列名的最佳数据类型。
  • @MartinSmith 感谢您指出逻辑问题,我现在已经解决了。我没有意识到他通过ROWCOUNT 函数限制了结果的数量。此外,我将变量更改为 sysname 并将 QUOTENAME 添加到动态 SQL
  • 感谢您的回复。我进行了@MartinSmith 建议的更改(颠倒调用顺序)。而这项工作。我没有测试你发布的内容。但感谢您的努力。
  • 没问题,最终你应该重写那个过程,因为CURSOR 绝对是错误的方法。
  • 这个答案完全是浪费时间,因为它没有回答如何修复这个错误的问题。如果他想知道如何重写这个查询,他可以问这个,这就是你可以提供的答案。任何寻找此错误答案的人都会被指向此处,因为它是热门搜索结果,因此感谢您浪费我的时间以及其他所有试图找到此错误答案的人。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-02-04
  • 2021-03-29
相关资源
最近更新 更多