2009-06-25 71 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 
     ... 
相關問題