Pregunta

Tengo una instrucción SQL dinámica que he creado en un procedimiento almacenado. Necesito iterar sobre los resultados usando un cursor. Me está costando descubrir 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 forma correcta de hacer esto?

¿Fue útil?

Solución

Un cursor solo aceptará una instrucción de selección, por lo que si el SQL realmente necesita ser dinámico, haga que el cursor de declaración forme 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 cursores globales, también puede insertar los resultados de su SQL dinámico en una tabla temporal y luego usar esa tabla para llenar su 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

Otros consejos

Este código es un muy buen ejemplo para una columna dinámica con un cursor, ya que no puede usar '+' en @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

Trabajar con una base de datos no relacional (¿alguien con IDMS?) a través de una conexión ODBC califica como uno de esos momentos en los que los cursores y el SQL dinámico parecen ser la única ruta.

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

tarda 45 minutos en responder mientras se reescribe para usar conjuntos de teclas sin que la cláusula in se ejecute 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 declaración in para la columna B contiene 1145 filas, usar un cursor para crear declaraciones indidivudales y ejecutarlas como SQL dinámico es mucho más rápido que usar la cláusula in. Tonta, ¿eh?

Y sí, no hay tiempo en una base de datos relacional para usar los cursores. Simplemente no puedo creer que me haya encontrado con una instancia en la que un bucle de cursor es varias magnitudes más rápido.

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

Debe haber 15 formas de perder sus cursores ... parte 1, Introducción

Procesamiento fila por fila sin cursor

Dicho esto, sin embargo, es posible que te quedes atrapado con uno después de todo; no sé lo suficiente de tu pregunta para asegurarme de que cualquiera de los dos se aplique. Si ese es el caso, tiene un problema diferente: la instrucción de selección para su cursor debe ser una instrucción SELECCIÓN real , no una instrucción EJECUTAR. Estás atrapado.

Pero vea la respuesta de cmsjr (que apareció mientras escribía) sobre el uso de una tabla temporal. Evitaría los cursores globales incluso más que & Quot; plain & Quot; unos ....

Después de cambiar recientemente de Oracle a SQL Server (preferencia del empleador), noto que la compatibilidad con el cursor en SQL Server está retrasada. Los cursores no siempre son malos, a veces se requieren, a veces mucho más rápido y a veces son más limpios que tratar de ajustar una consulta compleja reorganizando o agregando sugerencias de optimización. Los & Quot; los cursores son malvados & Quot; la opinión es mucho más prominente en la comunidad de SQL Server.

Entonces, supongo que esta respuesta es cambiar a Oracle o darle a MS una pista.

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

Otra opción en SQL Server es hacer todas sus consultas dinámicas en la variable de tabla en un proceso almacenado, luego usar un cursor para consultar y procesar eso. En cuanto al temido debate sobre el cursor :), he visto estudios que muestran que, en algunas situaciones, un cursor puede ser más rápido si está configurado correctamente. Los uso yo mismo cuando la consulta requerida es demasiado compleja, o simplemente no humanamente (para mí;)) posible.

este código puede ser útil para usted.

ejemplo de uso del cursor en el servidor 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 
      ...
Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top