50

我有一个在存储过程中创建的动态 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

这样做的正确方法是什么?

4

8 回答 8

125

游标将只接受一个选择语句,因此如果 SQL 确实需要动态,则将声明游标作为您正在执行的语句的一部分。为了使以下内容正常工作,您的服务器必须使用全局游标。

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
于 2009-06-25T20:01:02.910 回答
21

此代码是带有游标的动态列的一个很好的示例,因为您不能在 @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
于 2011-07-07T19:42:08.607 回答
4

通过 ODBC 连接使用非关系数据库(IDMS 有人吗?)是游标和动态 SQL 似乎是唯一途径的时代之一。

select * from a where a=1 and b in (1,2)

需要 45 分钟来响应,而重写以使用没有 in 子句的键集将在 1 秒内运行:

select * from a where (a=1 and b=1)
union all
select * from a where (a=1 and b=2)

如果 B 列的 in 语句包含 1145 行,则使用游标创建单个语句并作为动态 SQL 执行它们比使用 in 子句快得多。傻诶?

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

于 2010-09-22T21:44:41.183 回答
3

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

必须有 15 种方法来丢失光标...第 1 部分,简介

没有光标的逐行处理

就是说,尽管如此,您毕竟可能会遇到一个问题-我从您的问题中了解得不够多,无法确定其中任何一个都适用。如果是这种情况,您就会遇到不同的问题——游标的选择语句必须是实际的SELECT 语句,而不是 EXECUTE 语句。你被困住了。

但是请参阅 cmsjr(在我写作时出现)关于使用临时表的答案。我会避免使用全局游标,而不是“普通”游标......

于 2009-06-25T20:12:42.670 回答
3

我想与您分享另一个示例
:D http://www.sommarskog.se/dynamic_sql.html#cursor0

于 2011-07-07T15:19:33.107 回答
1

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

所以我猜这个答案是切换到Oracle或给MS一个线索。

于 2011-05-24T16:14:31.020 回答
0

SQL Server 中的另一个选项是对存储过程中的表变量进行所有动态查询,然后使用游标进行查询和处理。至于可怕的光标辩论:),我看到研究表明,在某些情况下,如果正确设置,光标实际上可以更快。当所需的查询太复杂,或者不是人为的(对我来说;))可能时,我自己会使用它们。

于 2019-05-09T22:19:50.403 回答
-3

此代码可能对您有用。

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 
      ...
于 2012-11-30T08:37:16.647 回答