Tsql 在存储过程中使用带有动态SQL的游标

Tsql 在存储过程中使用带有动态SQL的游标,tsql,stored-procedures,dynamic-sql,database-cursor,Tsql,Stored Procedures,Dynamic Sql,Database Cursor,我在存储过程中创建了一个动态SQL语句。我需要使用游标对结果进行迭代。我很难想出正确的语法。这是我正在做的 SELECT @SQLStatement = 'SELECT userId FROM users' DECLARE @UserId DECLARE users_cursor CURSOR FOR EXECUTE @SQLStatment --Fails here. Doesn't like this OPEN users_cursor FETCH NEXT FROM users_cu

我在存储过程中创建了一个动态SQL语句。我需要使用游标对结果进行迭代。我很难想出正确的语法。这是我正在做的

SELECT @SQLStatement = 'SELECT userId FROM users'

DECLARE @UserId

DECLARE users_cursor CURSOR FOR
EXECUTE @SQLStatment --Fails here. Doesn't like this

OPEN users_cursor
FETCH NEXT FROM users_cursor
INTO @UserId

WHILE @@FETCH_STATUS = 0
BEGIN

EXEC asp_DoSomethingStoredProc @UserId

END
CLOSE users_cursor
DEALLOCATE users_cursor

正确的方法是什么?

游标只接受select语句,因此,如果SQL确实需要是动态的,请将declare游标作为正在执行的语句的一部分。要使以下各项正常工作,您的服务器必须使用全局游标

Declare @UserID varchar(100)
declare @sqlstatement nvarchar(4000)
--move declare cursor into sql to be executed
set @sqlstatement = 'Declare  users_cursor CURSOR FOR SELECT userId FROM users'

exec sp_executesql @sqlstatement


OPEN users_cursor
FETCH NEXT FROM users_cursor
INTO @UserId

WHILE @@FETCH_STATUS = 0
BEGIN
Print @UserID
EXEC asp_DoSomethingStoredProc @UserId

FETCH NEXT FROM users_cursor --have to fetch again within loop
INTO @UserId

END
CLOSE users_cursor
DEALLOCATE users_cursor
如果需要避免使用全局游标,还可以将动态SQL的结果插入到临时表中,然后使用该表填充游标

Declare @UserID varchar(100)
create table #users (UserID varchar(100))

declare @sqlstatement nvarchar(4000)
set @sqlstatement = 'Insert into #users (userID) SELECT userId FROM users'
exec(@sqlstatement)

declare users_cursor cursor for Select UserId from #Users
OPEN users_cursor
FETCH NEXT FROM users_cursor
INTO @UserId

WHILE @@FETCH_STATUS = 0
BEGIN

EXEC asp_DoSomethingStoredProc @UserId

FETCH NEXT FROM users_cursor
INTO @UserId

END
CLOSE users_cursor
DEALLOCATE users_cursor

drop table #users

首先,尽可能避免使用光标。以下是一些资源,用于在您似乎离不开它时将其根除:

不过,也就是说,你可能终究还是会遇到一个问题——我从你的问题中了解的不多,无法确定这两个问题是否适用。如果是这种情况,则会遇到另一个问题——游标的select语句必须是实际的select语句,而不是EXECUTE语句。你被卡住了


但是看看cmsjr关于使用临时表的答案(我写这篇文章的时候它出现了)。与“普通”游标相比,我更倾向于避免使用全局游标……

通过ODBC连接使用非关系数据库(IDMS Anywhere?)符合以下条件:游标和动态SQL似乎是唯一的路径

select * from a where a=1 and b in (1,2)
在重新写入使用不带in子句的密钥集时,需要45分钟的响应时间,该子句将在1秒内运行:

select * from a where (a=1 and b=1)
union all
select * from a where (a=1 and b=2)
如果列B的in语句包含1145行,那么使用游标创建indidivudal语句并作为动态SQL执行它们要比使用in子句快得多。傻嘿


是的,在关系数据库中没有时间使用游标。我简直不敢相信我遇到过这样一个例子,游标循环快了好几个数量级。

在最近从Oracle切换到SQL Server(雇主偏好)之后,我注意到SQL Server中的游标支持滞后了。与试图通过重新安排或添加优化提示来优化复杂查询相比,游标并不总是邪恶的,有时是必需的,有时更快,有时更干净。“游标是邪恶的”观点在SQL Server社区中更为突出

所以我猜这个答案是转向甲骨文或者给微软一个线索

  • (a
    for
    循环隐式定义/打开/关闭光标!)

    • 我想与大家分享另一个例子
      :D

      这段代码是带有光标的动态列的一个很好的示例,因为您不能在@STATEMENT中使用“+”:

      ALTER PROCEDURE dbo.spTEST
      AS
          SET NOCOUNT ON
          DECLARE @query NVARCHAR(4000) = N'' --DATA FILTER
          DECLARE @inputList NVARCHAR(4000) = ''
          DECLARE @field sysname = '' --COLUMN NAME
          DECLARE @my_cur CURSOR
          EXECUTE SP_EXECUTESQL
              N'SET @my_cur = CURSOR FAST_FORWARD FOR
                  SELECT
                      CASE @field
                          WHEN ''fn'' then fn
                          WHEN ''n_family_name'' then n_family_name
                      END
                  FROM
                      dbo.vCard
                  WHERE
                      CASE @field
                          WHEN ''fn'' then fn
                          WHEN ''n_family_name'' then n_family_name
                      END
                      LIKE ''%''+@query+''%'';
                  OPEN @my_cur;',
              N'@field sysname, @query NVARCHAR(4000), @my_cur CURSOR OUTPUT',
              @field = @field,
              @query = @query,
              @my_cur = @my_cur OUTPUT
          FETCH NEXT FROM @my_cur INTO @inputList
          WHILE @@FETCH_STATUS = 0
          BEGIN
              PRINT @inputList
              FETCH NEXT FROM @my_cur INTO @inputList
          END
          RETURN
      

      这段代码对您很有用

      sql server中使用游标的示例

      DECLARE sampleCursor CURSOR FOR 
            SELECT K.Id FROM TableA K WHERE ....;
      OPEN sampleCursor
      FETCH NEXT FROM sampleCursor INTO @Id
      WHILE @@FETCH_STATUS <> -1
      BEGIN
      
      UPDATE TableB
         SET 
            ...
      
      DECLARE sampleCursor为
      从表K中选择K.Id,其中。。。。;
      打开样本光标
      从sampleCursor获取下一个到@Id
      而@@FETCH\u状态为-1
      开始
      更新表B
      设置
      ...
      
      SQL Server中的另一个选项是对存储过程中的表变量执行所有动态查询,然后使用游标查询和处理该变量。至于可怕的光标辩论:),我看到研究表明,在某些情况下,如果设置得当,光标实际上可以更快。当所需的查询太复杂时,或者只是不符合人性(对我来说;))时,我自己使用它们。

      正确的方法是不使用动态sql或游标。你能给出一个真实的例子来说明你想要实现什么吗?确切地说,在一个存储过程中没有两个大型SQL server:-)动态SQL不一定是邪恶的,特别是如果你使用sp_executesql而不是EXEC。但是,游标是被诅咒的。例如,如果您将目标数据库名称参数化,是否有替代动态SQL的方法?例如,进程与生产和测试环境数据库对话。避免使用游标-它们是邪恶的!:-)诅咒者不是邪恶的。资源密集型,不适合在生产场景中使用,当然。邪恶不。仅仅因为某些东西可能被错误地使用并不意味着你不应该知道如何使用它。也许我误读了这个问题,但它似乎是在问如何使用光标,而不是使用光标的利弊。我要说的是邪恶。。。。是的,OP询问如何使用光标。他还询问了“正确的方法”。负责任的回答是给出如何在没有光标的情况下完成最终结果的信息——这(几乎)总是可能的。这让我有能力编写完全无法理解的代码!在执行路径较远的非动态SQL中使用的动态生成SQL中声明的变量!我喜欢!很少有代码构造本质上是邪恶的(我在看COBOL,可重写代码是邪恶的),而游标不是其中之一。当然,初学者可能会因为不理解SQL中的处理集而尝试使用它们,但有时它们可以挽救生命-我已经开发了几个SSRS报告,您不可能以其他任何方式处理它们-仅供参考-这两篇文章都要求您现在注册查看。第二个示例非常糟糕。他创建存储过程和触发器只是为了避免使用游标。我怀疑他的方法是否比一个简单的正向游标更好/更有效。这对我来说很有效,并且避免了全局游标和临时表。谢谢,非常有用谢谢!。需要注意的是,游标似乎也必须在动态sql中打开——sql Server似乎认为游标未初始化,如果在动态内容之后执行,则会为我抛出一个错误。我看不出将
      @query
      连接到sql中会如何工作。我想你会得到像“%$xx”这样的
      ,假设
      @query
      设置为
      'xx'
      ,这是无效的sql。SP_EXECUTESQL的文档状态为:“@stmt必须是Unicode常量或Unicode变量”,这意味着您不能