Evan Barke

Tutorial de cursor de SQL Server

December 18, 2015 by

Introducción

La mayor parte de la gente que trabaja con Microsoft SQL Server habrá al menos oído de los cursores, y frecuentemente, incluso si la gente conoce en un nivel básico qué hacen los cursores de SQL Server, no siempre se sabe cuándo usarlos y cómo escribir el código detrás de ellos. Así que este artículo da un paso atrás y provee una explicación acerca de para qué se pueden usar los cursores de SQL Server así como un ejemplo básico que usted puede probar por sí mismo.

Programación Transaccional versus Procesal

SQL Server es un sistema de administración de bases de datos relacional (relational database management system, RDBMS) y T-SQL es un lenguaje de programación transaccional. Esto significa que está diseñado para ejecutar su trabajo en corridas de todo o nada. El motor de la base de datos es optimizado para trabajar de esta manera y, en general, es más que suficiente para ejecutar transacciones simples de tipo todo en uno.

Muchos otros lenguajes de programación incluyendo a C# y Visual Basic son lenguajes de programación iterativos o procesales, por lo cual el flujo general de cosas es tratar cada instancia de un objeto separadamente y cuando se trata con muchos objetos uno tendería a dar vueltas sobre el mismo código hasta que la pila es disminuida y procesada.

Sin embargo, los cursores, como los bucles WHILE se alejan de la naturaleza transaccional de T-SQL y permiten a los programadores tratar cada resultado de una sentencia SELECT en una cierta manera iterando a través de ellos.

En el mundo de la Ingeniería de IT es común que las personas aprendan lenguajes como C#, VB, Java, C++ o cualquier otro lenguaje de tipo iterativo antes de tener que lidiar con SQL en alguna manera real/avanzada. Es por esta razón, tristemente, que los cursores de SQL Server son frecuentemente muy prolíficos en algunas aplicaciones. Es una trampa común en la que los desarrolladores caen por alguna razón. La lógica detrás de los cursores puede ser perfecta y la idea de escribir uno puede parecer buena, pero uno se encuentra en verdaderos problemas cuando se trata de desempeño porque SQL Server ya no va a tratar pedazos completos de datos al mismo tiempo y en lugar de eso tiene que repetir las lecturas y escrituras para cada resultado (lo cual puede ser catastrófico para el desempeño I/O).

Por lo tanto, como una regla general y para un buen desempeño no use cursores.

De todos modos, hay algunas situaciones en las que los cursores pueden ser salva vidas. Puedo pensar en un par directamente:

  1. Consultas concurrentes: A veces, en los sistemas OLTP (Procesamiento de Transacciones en Línea, en inglés: OnLine Transaction Processing) hay demasiados usuarios consultando activamente una tabla específica. Esto está bien para transacciones pequeñas ya que son extremadamente rápidas y no requieren bloqueos o grandes pedazos de las tablas subyacentes. Pero para poder actualizar la tabla entera frecuentemente SQL Server tiene que crear un gran bloqueo que bloquea todas las otras actividades en la misma tabla. Esto es para proteger inconsistencia de datos. Si esto no fuera el caso un uso concurrente podría ocurrir y hacer un SELECT de las filas de la tabla que está actualizada sólo a la mitad. Hay opciones al nivel del servidor para manejar este tipo de lecturas, son llamadas niveles de aislamiento de transacción y están fuera del alcance de este artículo. De todas maneras, si uno tiene un nivel de aislamiento de transacción READ COMMITED el cual es el caso pro defecto, los cursores de SQL Server o bucles while puede ser útiles para dividir actualizaciones completas de tablas en múltiples partes más pequeñas.
  2. El segundo caso donde los cursores pueden ser útiles es para crear una lógica de tipo “for each” en scripts T-SQL. Por ejemplo, si uno quisiera manejar el despliegue de tablas individuales a otra base de datos uno podría usar un cursor y sp_executeSql para correr un pedazo de T-SQL para cada tabla en una lista dada.

En el ejemplo siguiente iteraremos a través del contenido de una tabla y seleccionaremos la descripción de cada ID que encontremos.

Tome esta simple tabla como un ejemplo:

Aquí están los resultados de una consulta SELECT * FROM #ITEMS:

Dialog showing the results of a SELECT * FROM #ITEMS query

Ahora digamos que deseamos cortar la selección de cada descripción en 5 transacciones separadas. Aquí está la sintaxis básica del cursor T-SQL para hacerlo.

Al correr el cursor básico SQL Server anterior iterará a través de cada ID en la tabla #ITEMS y hará un SELECT a su correspondiente ITEM_DECRIPTION en 5 transacciones separadas. Usted debería obtener los siguientes resultados después de ejecutar el cursor:

Dialog showing the results after executing the SQL Server cursor

Este ejemplo puede parecer sin sentido pero considere el hecho de que usted puede escribir cualquier T-SQL en medio.

Conclusión

Este artículo no pretende ser usado para proliferar el uso de cursores SQL Server incluso más en sus aplicaciones. Como regla general uno siempre debería pensar dos veces, incluso tres, acerca de si el uso de un cursor es aceptable para su problema actual. 99% de las veces el mismo problema puede ser resuelto en una manera puramente transaccional, como es la norma en T-SQL. Sin embargo, como se menciona anteriormente, hay ciertas excepciones a esa regla cuando es perfectamente aceptable sacrificar el desempeño para evitar bloquear la aplicación o sólo porque no hay otra opción (esto es un caso muy raro en IT).

Así que, si usted ha determinado que absolutamente necesita usar un cursor SQL Server, vaya adelante y básese en el ejemplo provisto aquí.

Recursos útiles
SET TRANSACTION ISOLATION LEVEL (Transact-SQL)
Using WHILE…BREAK or CONTINUE

Evan Barke

Evan Barke

Having worked on highly transactional production systems and advanced corporate business intelligence, Evan is now using this experience make a difference in the eHealth world. He is driven by the love of technology and a desire to solve complex problems creatively.

View all posts by Evan Barke
Evan Barke
2,327 Views