2009-09-25 10 views
34

Muchas veces cuando escribo procedimientos almacenados, etc. Utilizo un CURSOR al principio y luego encuentro algún problema de rendimiento con mi procedimiento.MS SQL Server - ¿Cuándo es bueno un CURSOR?

Todo lo que leo dice CURSORES son horribles, causan bloqueo innecesario, etc. y las pruebas de rendimiento demuestran lo mismo.

Mi pregunta es cuándo usas un CURSOR y en qué situaciones son útiles o buenas?

Si no sirve, ¿por qué crearían una estructura/tipo de control tan malo para SQL?

+5

@any: Quien haya marcado esta pregunta como "Exact Duplicate", ¿podría publicar el enlace a la pregunta original? Luego votaré para cerrar por "Exact Duplicate". Pero hasta entonces, encuentro que esta pregunta es informativa. – Sung

Respuesta

31

Normalmente deben evitarse, pero la función está ahí por una razón y hay momentos para usarlos. Diría que el 90% de los cursores que he visto no son necesarios. Si los está utilizando para operaciones CRUD, casi siempre se puede rehacer de una manera establecida. A menudo he visto personas usar cursores para esto porque no saben cómo usar uniones en una actualización o eliminación, o que pueden usar una declaración de selección en lugar de una cláusula de valores en una inserción. Otro uso innecesario cuando la gente piensa que lo necesita para un procesamiento un poco más complejo que en realidad podría manejarse fácilmente con una declaración de caso.

Los cursores son a veces más rápidos para calcular algo así como un total acumulado.

Los cursores también son útiles para múltiples ejecuciones de un proceso almacenado que está configurado para manejar solo un valor de entrada a la vez. No uso esta característica para ejecutar procs almacenados por el usuario (a menos que sepa que accederé a un conjunto muy pequeño de datos) pero es muy útil para los administradores de bases de datos cuando necesitan ejecutar procs del sistema contra múltiples tablas.

Si está creando correos electrónicos en SQl (no es el mejor lugar para hacerlo, pero en algunos sistemas es donde lo hacen) y no quiere que toda la audiencia del correo electrónico vea a otras personas en la lista o Quiero personalizar cada correo electrónico con información sobre el destinatario, los cursores son el camino a seguir.

Los cursores o bucles también se pueden utilizar para procesar lotes de registros si toda la inserción/actualización/eliminación basada en conjunto demorará demasiado y se bloquearán las tablas. Este es un tipo de híbrido entre los cursores y la solución basada en conjuntos y, a menudo, es el mejor para grandes cambios en los sistemas de producción.

+4

+1 por dar razones sólidas de por qué existen los cursores. Demasiadas personas piensan que la lógica basada en conjuntos de alguna manera (a) cubre todos los escenarios posibles (b) es siempre más eficaz que una solución basada en cursor, y (c) siempre es más fácil de mantener/leer. Si bien creo que la mayor parte del trabajo se realiza mejor mediante enfoques basados ​​en conjuntos, nada es nunca al 100%. –

+1

FWIW, creo que probablemente podría implementar un total acumulado con Common Table Expression si está utilizando SQL Server 2005/2008. – RMorrisey

+0

¿SQL Server tiene sintaxis de cursor implícita? Explícito es la declaración habitual con el enfoque OPEN, FETCH, CLOSE ... –

8

Le pregunté a un chico del equipo de SQL Server una vez, si pudiera agregar una característica que haría que el producto fuera mejor para todos, ¿cuál sería?

Su respuesta fue 'Añadir? Huh, me gustaría llevar uno. Si se deshace de los cursores, fuerza a los programadores de todo el mundo a comenzar a pensar en cosas con un sistema SET y ese será el mayor incremento mundial en rendimiento de BD que jamás haya visto ".

Por mi parte, sin embargo, tiendo a ver un patrón, parece que hay muchos codificadores de procedimientos que usan cursores porque necesitan poder hacer una operación de un elemento a la vez y echan de menos el antiguo concepto de bucle WHILE . La misma idea básica sin el cursor sobre su cabeza. Todavía no es tan rápido/efectivo como algo basado en SET, pero el 90% del tiempo cuando alguien dice 'No puedo hacer este conjunto basado, tengo que usar cursores'. Puedo hacer que lo hagan con un ciclo while.

+3

Son muy mal utilizados, que es a lo que probablemente se refirió ese tipo, pero la pregunta era sobre dónde usarlos. Ellos tienen un lugar en ciertos casos. –

+0

Al decir "la misma idea básica sin la sobrecarga del cursor", ¿sugiere que los bucles no tengan la misma sobrecarga que los cursores? – micahhoover

+0

El uso de un bucle WHILE para realizar una operación de un elemento a la vez en lugar de usar un cursor para realizar una operación de un elemento a la vez no cambia la operación a una operación basada en conjuntos. – WayTooSerious

0

Normalmente no uso los cursores, pero cuando lo hago, debe ser una consulta "única" que estoy ejecutando localmente o un trabajo diario. Desea evitar que el código de producción invoque un cursor que se invocaría con frecuencia como en respuesta a una solicitud web.

1

El manual de preparación de MCTS para SQL Server 2008 que estoy estudiando recomienda utilizar un código CLR externo en cualquier lugar en que se requiera un CURSOR en T-SQL, especialmente ahora que SQL Server 2008 admite funciones agregadas personalizadas.

Hace 5 años, trabajé con ellos para obtener amplias funciones de generación de informes, pero no creo que pueda encontrar un buen caso de uso para ellos ahora. Los agregados CLR y las funciones funcionan de forma similar a las funciones agregadas integradas.

+0

Esa no ha sido mi experiencia en absoluto. La invocación de métodos CLR parece tener su propia sobrecarga, y si llama a dicho método en un bucle que se ejecuta con un volumen lo suficientemente significativo, las cosas tienden a ralentizarse. ¿Cómo han sido las cosas de tu lado? –

+0

Bastante bien hasta ahora, aunque debo admitir que he estado usando funciones simples. ¿Estás hablando de agregados específicamente? – womp

+1

Los agregados CLR serían más útiles si se implementara ['IsInvariantToOrder'] (http://msdn.microsoft.com/en-us/library/microsoft.sqlserver.server.sqluserdefinedaggregateattribute.isinvarianttoorder.aspx) en lugar de reservados para uso futuro . Algunas cifras de rendimiento que respaldan CLR vs Cursors [están aquí] (http: // sqlblog.com/blogs/adam_machanic/archive/2006/07/12/running-sums-yet-again-sqlclr-saves-the-day.aspx) –

0

Los cursores son útiles cuando 1) necesita hacer algo que no puede hacer con una operación establecida, o 2) no tiene sentido hacer el mismo trabajo al hacer llamadas iterativas desde la capa de aplicación. O a veces tiene un procedimiento que debe permanecer en la capa de la base de datos, y simplemente no puede volver a la capa intermedia de la aplicación para iterar sobre algún conjunto de resultados.

Una recomendación que haría, es que la gente usa variables de cursor en lugar de cursores normales, porque evita los problemas de asignación/desasignación del cursor que rodean los cursores normales. Con un cursor normal, si no los desasigna, persisten, lo que puede ser una fuente de pérdidas de memoria. No ocurre lo mismo con los cursores basados ​​en variables (es decir, DECLARE @cursor CURSOR).

En resumen, evítelos si es posible, y si no puede, utilícelos mínimamente y con prudencia.

1

La única vez que los usaré es cuando lo que se hace dentro del cursor tiene que hacerse un ítem a la vez y donde lo que se hace dentro del cursor toma tanto tiempo que la altura del cursor se desvanece en insignificancia.

Por ejemplo, copias de seguridad de bases de datos, verificaciones de integridad, reconstrucciones de índices. En resumen, tareas de administración.

0

Utilice el cursor para reconstruir o reorganizar índices de tabla individualmente
a menos que haya una forma de ejecutar ALTER INDEX... como una operación basada en conjuntos.

1

OMG, ¿cómo me olvidé de Group By? Tomé la consulta basada en el cursor que ves a continuación y la reemplacé por la que está después. Ahora obtengo un único conjunto de resultados para que no haya problemas con el uso de sqlsrv_next_result() en php.

DECLARE @thisday datetime; 

DECLARE daycursor CURSOR FOR 
SELECT DISTINCT DATEADD(day, 0, DATEDIFF(day, 0, TimeCollected)) as thisday 
FROM computerusedata 

OPEN daycursor; 
FETCH NEXT FROM daycursor 
INTO @thisday; 
WHILE @@FETCH_STATUS = 0 
    BEGIN 
    select distinct left(ComputerName,5) as CompGroup,DATEADD(day, 0, DATEDIFF(day, 0, TimeCollected)) as day 
    FROM computerusedata 
    where DATEADD(day, 0, DATEDIFF(day, 0, TimeCollected)) = @thisday 
    order by CompGroup; 
    FETCH NEXT FROM daycursor; 
    END; 
CLOSE daycursor; 
DEALLOCATE daycursor;"; 


select DATEADD(day, 0, DATEDIFF(day, 0, TimeCollected)) as day,left(ComputerName,5) as CompGroup 
from ComputerUseData 
group by DATEADD(day, 0, DATEDIFF(day, 0, TimeCollected)),left(ComputerName,5) 
order by day,CompGroup 
Cuestiones relacionadas