Uso de un cursor con SQL dinámico en un procedimiento almacenado


Tengo una instrucción SQL dinámica que he creado en un procedimiento almacenado. Necesito iterar sobre los resultados usando un cursor. Estoy teniendo dificultades para encontrar la sintaxis correcta. Esto es lo que estoy haciendo.

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

¿Cuál es la manera correcta de hacer esto?

Author: Leniel Maccaferri, 2009-06-25

7 answers

Un cursor solo aceptará una instrucción select, por lo que si el SQL realmente necesita ser dinámico, haga que el cursor declare sea parte de la instrucción que está ejecutando. Para que lo siguiente funcione, su servidor tendrá que usar cursores globales.

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

Si necesita evitar el uso de los cursores globales, también puede insertar los resultados de su SQL dinámico en una tabla temporal, y luego usar esa tabla para rellenar el cursor.

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
 98
Author: cmsjr,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2011-06-01 01:29:33

Este código es un muy buen ejemplo para una columna dinámica con un cursor, ya que no puede usar ' + ' en la INSTRUCCIÓN@:

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
 15
Author: SMHMayboudi,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2011-07-08 18:30:49

Trabajar con una base de datos no relacional(IDMS anyone?) sobre una conexión ODBC califica como una de esas veces donde cursores y SQL dinámico parece la única ruta.

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

Tarda 45 minutos en responder, mientras que reescrito para usar conjuntos de claves sin la cláusula in se ejecutará en menos de 1 segundo:

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

Si la instrucción in para la columna B contiene 1145 filas, usar un cursor para crear instrucciones indidivudales y ejecutarlas como SQL dinámico es mucho más rápido que usar la cláusula in. Tonto hey?

Y sí, no hay tiempo en una base de datos relacional que el cursor debe ser utilizado. No puedo creer que me haya encontrado con una instancia en la que un bucle de cursor es varias magnitudes más rápido.

 4
Author: Twelfth,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2011-06-01 01:34:03

En primer lugar, evite usar un cursor si es posible. Aquí hay algunos recursos para erradicarlo cuando parece que no puedes prescindir de él:

Debe Haber 15 Maneras De Perder Tus Cursores... parte 1, Introducción

Procesamiento Fila Por Fila Sin Cursor

Dicho esto, sin embargo, usted puede estar atascado con uno después de todo I No se lo suficiente de su pregunta para estar seguro de que cualquiera de los dos se aplican. Si ese es el caso, tienes un problema diferente select el select la instrucción para el cursor debe ser una real SELECT, no una instrucción EXECUTE. Estás atascado.

Pero vea la respuesta de cmsjr (que llegó mientras escribía) sobre el uso de una tabla temporal. Evitaría los cursores globales incluso más que los "simples"....

 3
Author: RolandTumble,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2009-06-25 20:12:42

Después de cambiar recientemente de Oracle a SQL Server (preferencia del empleador), noto que el soporte del cursor en SQL Server está retrasado. Los cursores no siempre son malos, a veces son necesarios, a veces mucho más rápidos y a veces más limpios que intentar ajustar una consulta compleja reorganizando o agregando sugerencias de optimización. La opinión de" cursors are evil " es mucho más prominente en la comunidad de SQL Server.

Así que supongo que esta respuesta es cambiar a Oracle o darle a MS una pista.

 1
Author: crokusek,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-05-23 12:02:45

Hay otro ejemplo que me gustaría compartir con ustedes
:D http://www.sommarskog.se/dynamic_sql.html#cursor0

 1
Author: SMHMayboudi,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2011-07-07 15:19:33

Este código puede ser útil para usted.

Ejemplo de uso del cursor en 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 
      ...
 -3
Author: carlito,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2013-05-17 16:22:29