Saltar al contenido

Pros y contras de usar un cursor (en el servidor SQL)

Esta duda se puede abordar de variadas maneras, sin embargo te damos la que en nuestra opinión es la solución más completa.

Solución:

Hay varios escenarios en los que los cursores funcionan mejor que los equivalentes basados ​​en conjuntos. Los totales acumulados son los que siempre vienen a la mente: busque las palabras de Itzik al respecto (e ignore cualquiera que involucre a SQL Server 2012, que agrega nuevas funciones de ventanas que hacen que los cursores corran por su dinero en esta situación).

Uno de los grandes problemas que tiene la gente con los cursores es que funcionan lentamente, usan almacenamiento temporal, etc. Esto se debe en parte a que la sintaxis predeterminada es un cursor global con todo tipo de opciones predeterminadas ineficientes. La próxima vez que esté haciendo algo con un cursor que no necesita hacer cosas como UPDATE...WHERE CURRENT OF (que he podido evitar durante toda mi carrera), dale una oportunidad comparando estas dos opciones de sintaxis:

DECLARE c CURSOR 
    FOR ;

De hecho, la primera versión representa un error en el procedimiento almacenado no documentado sp_MSforeachdb que hace que omita bases de datos si el estado de alguna de ellas cambia durante la ejecución. Posteriormente escribí mi propia versión del procedimiento almacenado (ver aquí) que solucionó el error (simplemente usando la última versión de la sintaxis anterior) y agregué varios parámetros para controlar qué bases de datos se elegirían.

Mucha gente piensa que una metodología no es un cursor porque no dice DECLARE CURSOR. He visto a personas argumentar que un ciclo while es más rápido que un cursor (que espero haber disipado aquí) o que usar FOR XML PATH realizar la concatenación de grupos no es realizar una operación de cursor oculto. Mirar el plan en muchos casos mostrará la verdad.

En muchos casos, los cursores se usan donde el conjunto basado es más apropiado. Pero hay muchos casos de uso válidos en los que un equivalente basado en conjuntos es mucho más complicado de escribir, para que el optimizador genere un plan para ambos, o no sea posible (por ejemplo, tareas de mantenimiento en las que está recorriendo tablas para actualizar estadísticas, llamando a un procedimiento almacenado para cada valor en un resultado, etc.). lo mismo es true para muchas consultas grandes de varias tablas donde el plan se vuelve demasiado monstruoso para que el optimizador lo maneje. En estos casos, puede ser mejor volcar primero algunos de los resultados intermedios en una estructura temporal. Lo mismo ocurre con algunos equivalentes de cursores basados ​​en conjuntos (como los totales acumulados). También he escrito sobre la otra manera, donde la gente casi siempre piensa instintivamente en usar un bucle/cursor while y hay alternativas inteligentes basadas en conjuntos que son mucho mejores.

ACTUALIZACIÓN 2013-07-25

Solo quería agregar algunas publicaciones de blog adicionales que he escrito sobre cursores, qué opciones debería usar si hacer tiene que usarlos, y usar consultas basadas en conjuntos en lugar de bucles para generar conjuntos:

Mejores enfoques para acumular totales: actualizado para SQL Server 2012

¿Qué impacto pueden tener las diferentes opciones del cursor?

Genere un conjunto o secuencia sin bucles: [Part 1] [Part 2] [Part 3]

El problema con los cursores en SQL Server es que el motor se basa internamente en conjuntos, a diferencia de otros DBMS como Oracle, que se basan internamente en cursores. Esto significa que cuando crea un cursor en SQL Server, se debe crear un almacenamiento temporal y se debe copiar el conjunto de resultados basado en conjuntos en el almacenamiento temporal del cursor. Puede ver por qué esto sería costoso desde el principio, sin mencionar el procesamiento fila por fila que podría estar haciendo sobre el cursor. La conclusión es que el procesamiento basado en conjuntos es más eficiente y, a menudo, su operación basada en el cursor se puede realizar mejor utilizando una tabla temporal o CTE.

Dicho esto, hay casos en los que un cursor probablemente sea aceptable, como dijiste para operaciones únicas. El uso más común en el que puedo pensar es en un plan de mantenimiento en el que puede estar iterando a través de todas las bases de datos en un servidor ejecutando varias tareas de mantenimiento. Siempre que limite su uso y no diseñe aplicaciones completas en torno al procesamiento RBAR (fila por fila agonizante), debería estar bien.

En general, los cursores son algo malo. Sin embargo, en algunos casos es más práctico usar un cursor y en algunos es aún más rápido usar uno. Un buen ejemplo es un cursor a través de una tabla de contactos que envía correos electrónicos según algunos criterios. (No abrir la pregunta si enviar un correo electrónico desde su DBMS es una buena idea; supongamos que es para el problema en cuestión). No hay forma de escribir eso basado en conjuntos. Podría usar algunos trucos para encontrar una solución basada en conjuntos para generar SQL dinámico, pero no existe una solución basada en conjuntos real.

Sin embargo, se puede realizar un cálculo que involucre la fila anterior mediante una autocombinación. Eso suele ser aún más rápido que un cursor.

En todos los casos, debe equilibrar el esfuerzo que implica desarrollar una solución más rápida. Si a nadie le importa, si procesa ejecuciones en 1 minuto o en una hora, use lo que haga el trabajo más rápido. Si está recorriendo un conjunto de datos que crece con el tiempo como un [orders] tabla, trate de mantenerse alejado de un cursor si es posible. Si no está seguro, realice una prueba de rendimiento comparando una base de cursor con una solución basada en conjuntos en varios tamaños de datos significativamente diferentes.

Comentarios y puntuaciones de la guía

Al final de la web puedes encontrar las acotaciones de otros usuarios, tú aún puedes insertar el tuyo si dominas el tema.

¡Haz clic para puntuar esta entrada!
(Votos: 0 Promedio: 0)



Utiliza Nuestro Buscador

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *