2012-02-16 22 views
7

me hizo una pregunta que aquí Using cursor in OLTP databases (SQL server)Pros y contras del uso de un cursor (en el servidor SQL)

donde respondieron diciendo cursores nunca se deben usar las personas.

Creo que los cursores son herramientas muy potentes que deben utilizarse (no creo que Microsoft admita cursores para desarrolladores malos). Supongamos que tiene una tabla donde el valor de una columna en una fila depende del valor de la misma columna en la fila anterior. Si se trata de un proceso final de una sola vez, ¿no cree que usar un cursor sería una opción aceptable?

En la parte superior de mi cabeza puedo pensar en un par de escenarios en los que siento que no debería haber vergüenza al usar los cursores. Por favor, avíseme si ustedes sienten lo contrario.

1> Un proceso final de una vez para limpiar los datos incorrectos que completa la ejecución en pocos minutos. 2> Procesos por lotes que se ejecutan una vez en un período de tiempo prolongado (algo así como una vez al año). Si en los escenarios anteriores, no hay una tensión visible en los otros procesos, ¿no sería irracional pasar más horas escribiendo código para evitar los cursores? En otras palabras, en ciertos casos, el tiempo del desarrollador es más importante que la ejecución de un proceso que casi no tiene impacto en ninguna otra cosa.

En mi opinión, estos serían algunos escenarios en los que debería tratar de evitar el uso del cursor. 1> Un proceso almacenado llamado desde un sitio web al que se puede llamar con mucha frecuencia. 2> Un trabajo SQL que se ejecutaría varias veces al día y consumiría una gran cantidad de recursos.

Creo que es muy superficial hacer una declaración general como "los cursores nunca deben usarse" sin analizar la tarea en cuestión y ponderarla con las alternativas.

Háganme saber sus comentarios.

+1

Parece que Sql Server 2012 admitirá el caso de obtener el valor de la fila anterior de una manera establecida. – automatic

+1

"Un procedimiento almacenado llamado desde un sitio web" es demasiado vago. Si un cursor es la opción más razonable para resolver un problema, y ​​el procedimiento debe ser llamado desde una página web ... –

Respuesta

11

Existen varios escenarios en los que los cursores funcionan mejor que los equivalentes basados ​​en conjuntos. Ejecutar totales es lo que siempre viene a la mente: busque las palabras de Itzik sobre eso (e ignore cualquiera que involucre a SQL Server 2012, que agrega nuevas funciones de ventana que dan curso a los cursores por su dinero en esta situación).

Uno de los grandes problemas que tienen las personas 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 ineficaces. La próxima vez que esté haciendo algo con un cursor que no necesite hacer cosas como UPDATE...WHERE CURRENT OF (que he podido evitar durante toda mi carrera), pruébelo comparando estas dos opciones de sintaxis:

DECLARE c CURSOR 
    FOR <SELECT QUERY>; 

DECLARE c CURSOR 
    LOCAL STATIC READ_ONLY FORWARD_ONLY 
    FOR <SELECT QUERY>; 

De hecho, la primera versión representa un error en el procedimiento almacenado no documentado sp_MSforeachdb que hace que omita las bases de datos si el estado de cualquier base de datos cambia durante la ejecución. Posteriormente, escribí mi propia versión del procedimiento almacenado (véanse here y here) que corrigió el error (simplemente utilizando la última versión de la sintaxis anterior) y agregó 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 personas que argumentan que un ciclo while es más rápido que un cursor (which I hope I've dispelled here) o que usar FOR XML PATH para realizar la concatenación de grupo no está realizando una operación de cursor oculto. Ver el plan en muchos casos mostrará la verdad.

En muchos casos, los cursores se usan cuando el conjunto es más apropiado. Pero hay muchos casos de uso válidos en los que un equivalente basado en conjunto es mucho más complicado de escribir, para que el optimizador genere un plan para ambos, o no posible (por ejemplo, tareas de mantenimiento en las que se recorren tablas para actualizar estadísticas, llamando a un procedimiento almacenado para cada valor en un resultado, etc.). Lo mismo es cierto para muchas consultas grandes de tablas múltiples donde el plan se vuelve demasiado monstruoso para que lo maneje el optimizador. En estos casos, puede ser mejor descargar primero algunos de los resultados intermedios en una estructura temporal. Lo mismo ocurre con algunos equivalentes basados ​​en conjuntos de cursores (como totales acumulados). También escribí sobre el otro lado, donde la gente casi siempre piensa instintivamente usar un ciclo while/cursor y hay clever set-based alternatives that are much better.

ACTUALIZACIÓN 2013-07-25

sólo quería añadir algunas entradas de blog adicionales que he escrito acerca de los cursores, las opciones que debe utilizar si hacer tienen que usarlos, y utilizando encomendadas a partir- dudas en lugar de bucles para generar conjuntos:

Best Approaches for Running Totals - Updated for SQL Server 2012

What impact can different cursor options have?

generar un conjunto o secuencia sin Loops: [Part 1][Part 2][Part 3]

6

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

Dicho esto, hay casos en los que un cursor es probablemente aceptable, como ha dicho para operaciones únicas. El uso más común que se me ocurre es en un plan de mantenimiento en el que puede iterar a través de todas las bases de datos en un servidor que ejecuta varias tareas de mantenimiento. Siempre y cuando limite su uso y no diseñe aplicaciones completas en torno al procesamiento RBAR (fila por agonizing-row), debería estar bien.

3

En general, los cursores son una mala cosa. 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 basados ​​en algunos criterios. (No para abrir la pregunta si el envío de un correo electrónico de su SGBD es una buena idea, supongamos que es por el problema en cuestión). No hay forma de escribir esa configuración. Podría utilizar algunos trucos para llegar a una solución basada en conjuntos para generar SQL dinámico, pero no existe una solución real basada en conjuntos.

Sin embargo, un cálculo que involucra la fila anterior se puede hacer mediante una autocombinacion. Eso usualmente es aún más rápido que un cursor.

En todos los casos, necesita equilibrar el esfuerzo que supone desarrollar una solución más rápida. Si a nadie le importa, si procesa se ejecuta en 1 minuto o en una hora, use lo que hace el trabajo más rápido. Si está recorriendo un conjunto de datos que crece con el tiempo como una tabla [pedidos], 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.

+1

+1 Estoy de acuerdo con mucho de lo que ha dicho, aunque los cálculos de filas anteriores pueden ser bastante complejos para escriba con la mayoría de las soluciones basadas en conjuntos, por ejemplo, ejecutando totales (descartando la funcionalidad de prelanzamiento por ahora). –

0

Son necesarios para cosas como el pivoteo de SQL dinámico, pero debe intentar y evitar usarlos siempre que sea posible.

0

Siempre me han disgustado los cursores debido a su bajo rendimiento. Sin embargo, descubrí que no entendía completamente los diferentes tipos de cursores y que, en ciertos casos, los cursores son una solución viable.

Cuando tiene un problema de negocios que solo se puede resolver procesando una fila a la vez, entonces es apropiado un cursor.

Para mejorar el rendimiento con el cursor, cambie el tipo de cursor que está utilizando. Algo que no sabía era que, si no especificas qué tipo de cursor estás declarando, obtienes el tipo Dynamic Optimistic por defecto, que es el más lento para el rendimiento porque está haciendo mucho trabajo bajo el capó . Sin embargo, al declarar el cursor como un tipo diferente, digamos un cursor estático, tiene muy buen rendimiento.

Consulte estos artículos para una explicación más completa:

The Truth About Cursors: Part I

The Truth About Cursors: Part II

The Truth About Cursors: Part III

creo que el mayor golpe contra los cursores es el rendimiento, sin embargo, no echando a cabo una tarea en una el enfoque basado en el conjunto probablemente ocupe el segundo lugar. Tercero sería la legibilidad y el diseño de las tareas, ya que generalmente no tienen muchos comentarios útiles.

SQL Server está optimizado para ejecutar el enfoque basado en conjunto. Usted escribe la consulta para devolver un conjunto de datos resultante, como un join en las tablas, por ejemplo, pero el motor de ejecución de SQL Server determina qué combinación usar: Merge Join, Nested Loop Join o Hash Join. SQL Server determina el mejor algoritmo de unión posible basado en las columnas participantes, el volumen de datos, la estructura de indexación y el conjunto de valores en las columnas participantes. Por lo tanto, utilizar un enfoque basado en conjuntos suele ser el mejor enfoque en cuanto a rendimiento en comparación con el método de cursor de procedimientos.

Cuestiones relacionadas