2009-06-25 54 views
38

我有一个在存储过程中创建的动态SQL语句。我需要使用游标来遍历结果。我很难搞清楚正确的语法。这是我正在做的。在存储过程中使用带有动态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 

什么是正确的方法来做到这一点?

+2

光标使用

示例中的“正确”的方法是不使用动态SQL或游标。你能举一个你想要完成的事情的真实例子吗? – NotMe 2009-06-25 20:19:12

+0

准确地说 - 单个存储过程中SQL服务器的两个大NO-NO :-) – 2009-06-25 20:29:50

+1

动态SQL不一定是邪恶的,特别是如果您使用sp_executesql而不是EXEC。游标,虽然被诅咒。 – RolandTumble 2009-06-25 21:53:24

回答

95

游标只会接受select语句,所以如果SQL确实需要动态化,请将执行语句的declare cursor部分。对于下面的工作你的服务器将不得不使用全局游标。

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 
3

首先,尽量避免使用游标。这里有一些资源铲除它出来的时候,似乎你不能没有:

There Must Be 15 Ways To Lose Your Cursors... part 1, Introduction

Row-By-Row Processing Without Cursor

这就是说,虽然,你可能会用一个卡毕竟 - 我不从你的问题中得到足够的了解,以确保其中任何一个适用。如果是这样的话,你会遇到一个不同的问题 - 光标的select语句必须是实际的 SELECT语句,而不是EXECUTE语句。你被卡住了。

但请参阅cmsjr(我在写作时提供)有关使用临时表的答案。我会避免全球光标不是“普通”的人更是....

4

与非关系型数据库(IDMS人?)通过ODBC连接工作资格作为那个时代哪里游标和动态SQL的一个似乎是唯一的途径。

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

需要45分钟,而重新编写,使用按键组没有条款将运行在1秒以内回应:

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

如果在B列语句包含1145行,使用光标创建indidivudal语句并执行它们,因为动态SQL比使用in子句快得多。傻嗨?

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

1

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

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

14

这段代码是一个带有光标的动态列的很好的例子,因为你不能se'+'in @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 ''%''[email protected]+''%''; 
      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 
-2

此代码可能对您有用。在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 
     ... 
相关问题