我有一个我在存储过程中创建的动态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
这样做的正确方法是什么?
答案 0 :(得分:109)
游标只接受select语句,因此如果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
答案 1 :(得分:18)
这段代码是带光标的动态列的一个很好的例子,因为你不能在@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
答案 2 :(得分:4)
通过ODBC连接使用非关系数据库(IDMS任何人?)可以作为游标和动态SQL似乎是唯一路径的时间之一。
select * from a where a=1 and b in (1,2)
需要45分钟才能响应,同时重新编写使用keysets而没有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行,则使用游标创建indidivudal语句并将其作为动态SQL执行,这比使用in子句要快得多。傻嘿?
是的,在关系数据库中没有时间应该使用游标。我简直无法相信我遇到了一个光标循环速度快几倍的实例。
答案 3 :(得分:3)
首先,尽可能避免使用光标。这里有一些资源可以帮助你解决这个问题:
There Must Be 15 Ways To Lose Your Cursors... part 1, Introduction
Row-By-Row Processing Without Cursor
尽管如此,毕竟你可能会遇到一个问题 - 我对你的问题不够了解,以确保其中任何一个适用。如果是这种情况,则会遇到另一个问题 - 游标的select语句必须是实际 SELECT语句,而不是EXECUTE语句。你被困住了。
但是看看cmsjr(在我写作的时候出现的)关于使用临时表的答案。我会避免使用 global 游标,而不仅仅是“普通”游标......
答案 4 :(得分:1)
最近从Oracle切换到SQL Server(雇主偏好)后,我注意到SQL Server中的游标支持滞后。游标并不总是邪恶的,有时需要,有时要快得多,有时比通过重新安排或添加优化提示来调整复杂查询更清晰。 “游标是邪恶的”意见在SQL Server社区中更为突出。
所以我猜这个答案是切换到Oracle或给MS一个线索。
for
循环隐式定义/打开/关闭光标!)答案 5 :(得分:1)
我还想与你分享另一个例子:D http://www.sommarskog.se/dynamic_sql.html#cursor0
答案 6 :(得分:0)
SQL Server中的另一个选项是对存储的proc中的表变量进行所有动态查询,然后使用游标进行查询和处理。关于可怕的游标辩论:),我看到的研究表明,在某些情况下,如果正确设置游标,实际上可以更快。当要求的查询过于复杂,或者对人类而言(对我自己;)不可行时,我自己使用它们。
答案 7 :(得分:-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
...