Domanda

Ho un'istruzione SQL dinamica che ho creato in una procedura memorizzata. Devo scorrere i risultati usando un cursore. Sto facendo fatica a capire la sintassi giusta. Ecco cosa sto facendo.

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

Qual è il modo giusto per farlo?

È stato utile?

Soluzione

Un cursore accetterà solo un'istruzione select, quindi se l'SQL ha davvero bisogno di essere dinamico, rendi il cursore dichiarare parte dell'istruzione che stai eseguendo. Perché il seguito funzioni, il tuo server dovrà utilizzare i cursori globali.

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

Se devi evitare di usare i cursori globali, puoi anche inserire i risultati del tuo SQL dinamico in una tabella temporanea, quindi utilizzare quella tabella per popolare il cursore.

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

Altri suggerimenti

Questo codice è un ottimo esempio per una colonna dinamica con un cursore, poiché non è possibile utilizzare '+' 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 ''%''+@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

Lavorare con un database non relazionale (chiunque IDMS?) su una connessione ODBC si qualifica come una di quelle volte in cui cursori e SQL dinamico sembrano l'unica strada.

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

impiega 45 minuti per rispondere mentre riscritto per usare i keyset senza la clausola in verrà eseguito in meno di 1 secondo:

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

Se l'istruzione in per la colonna B contiene 1145 righe, l'utilizzo di un cursore per creare istruzioni indipendenti ed eseguirle come SQL dinamico è molto più veloce rispetto all'utilizzo della clausola in. Ehi sciocco?

E sì, non c'è tempo in un database relazionale che il cursore debba essere usato. Non riesco proprio a credere di essermi imbattuto in un'istanza in cui un loop del cursore è più veloce di molte magnitudini.

Prima di tutto, evita di usare un cursore se possibile. Ecco alcune risorse per sradicarlo quando sembra che non puoi farne a meno:

Devono esserci 15 modi per perdere i cursori ... parte 1, Introduzione

Elaborazione riga per riga senza cursore

Detto questo, però, potresti essere bloccato con uno dopo tutto - non so abbastanza dalla tua domanda per essere sicuro che uno di questi si applichi. In tal caso, hai un problema diverso: l'istruzione select per il cursore deve essere un'istruzione SELECT effettiva , non un'istruzione EXECUTE. Sei bloccato.

Ma vedi la risposta di cmsjr (che è arrivata mentre stavo scrivendo) sull'uso di una tabella temporanea. Eviterei i cursori globali anche più di & Quot; plain & Quot; quelli ....

Dopo il recente passaggio da Oracle a SQL Server (preferenza del datore di lavoro), noto che il supporto del cursore in SQL Server è in ritardo. I cursori non sono sempre malvagi, a volte richiesti, a volte molto più veloci e talvolta più puliti rispetto al tentativo di ottimizzare una query complessa riorganizzando o aggiungendo suggerimenti di ottimizzazione. I & Quot; i cursori sono malvagi & Quot; L'opinione è molto più importante nella comunità di SQL Server.

Quindi suppongo che questa risposta sia passare a Oracle o dare un indizio a MS.

C'è un altro esempio che vorrei condividere con te
: D http://www.sommarskog.se/dynamic_sql.html#cursor0

Un'altra opzione in SQL Server è di eseguire tutte le query dinamiche in una variabile di tabella in un proc memorizzato, quindi utilizzare un cursore per eseguire query ed elaborarlo. Per quanto riguarda il temuto dibattito sul cursore :), ho visto studi che mostrano che in alcune situazioni, un cursore può effettivamente essere più veloce se impostato correttamente. Li uso io stesso quando la query richiesta è troppo complessa o semplicemente non umanamente (per me;)) possibile.

questo codice può essere utile per te.

esempio di utilizzo del cursore nel server sql

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 
      ...
Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top