Twitter Facebook RSS Feed

jueves, 26 de noviembre de 2009 a las 10:27hs por Dario Krapp

Opciones y más opciones

Hasta este momento todo es muy sencillo, una pregunta que al menos a mí me parece interesante de hacernos es ¿qué sucederá si mientras estamos recorriendo los registros en nuestro cursor los datos son modificados externamente?, para poder representar este escenario una posibilidad es ejecutar el cursor que habíamos definido previamente en modo debug en el SQL Server Management Studio, como se muestra a continuación:


Cursor en modo debug

Si mientras los datos son recorridos por el cursor, (En el ejemplo puede verse que la variable @Description ha tomado el valor “Serious back-country riding….”) en forma paralela ejecutamos una consulta de actualización (mientras dejamos el cursor esperando en la sentencia FETCH) desde otro SQL Server Management Studio, como se muestra en el siguiente ejemplo:


Query Externo

Notaremos que en la próxima iteración cuando el comando FETCH es ejecutado, los cambios actualizados externamente serán automáticamente leídos. En el ejemplo que hemos propuesto, puede verse que la variable @Description ha tomado el valor “!A true multi-sport bike that offers…” agregándose el signo “!” al comienzo del contenido del campo Description. Si probamos eliminar o insertar registros notaremos que los cambios son también reflejados.


Cursor Dynamic

Este comportamiento dependerá del tipo de cursor a crearse y se determinará en el momento de su definición, para este caso específico, el tipo de cursor se denominará cursor dinámico y es la definición asumida por defecto, aunque puede definirse explícitamente de la siguiente forma:

DECLARE ProdInfo CURSOR DYNAMIC FOR SELECT….

Otra posibilidad disponible es la siguiente:

DECLARE ProdInfo CURSOR STATIC FOR SELECT…..

Si realizamos las mismas pruebas veremos que el comportamiento del cursor es completamente distinto y la actualización (tanto como las inserciones y eliminaciones de registros) externas serán ignorada hasta que el cursor sea reabierto.

La tercera opción disponible es la denominada KEYSET que permitirá al cursor detectar las actualizaciones y eliminaciones, pero no registros insertados, la forma de definir un cursor KEYSET es la siguiente:

DECLARE ProdInfo CURSOR KEYSET FOR SELECT….

Hay un detalle que creo que es muy interesante en este punto, para poder dar un ejemplo, supongamos que poseemos la tabla llamada Ejemplo1 (con unos pocos registros insertados), como se muestra en la imagen a continuación:


Tabla Ejemplo1

Y definimos un cursor KEYSET que recorrerá la tabla mientras que paralelamente realizamos modificaciones (UPDATES) externamente sobre los registros que el cursor va recorriendo, al igual que lo habíamos hecho previamente sobre la tabla Production.ProductDescription.

Si hacemos esta prueba veremos que algo extraño sucede, y es que el cursor se comporta como un cursor STATIC a pesar de haberse definido como KEYSET. Si observamos más detalladamente la estructura de la tabla Ejemplo1 notaremos otro detalle, y es que no hay clave primaria definida. Si definimos una clave primaria (obviamente sobre la columna ID en este caso) todo funcionará según lo esperado.
En este caso aunque a primera vista este comportamiento parezca incomprensible, es en realidad bastante lógico, ya que todo cursor KEYSET copiará las claves de la consulta que recorrerá en una tabla temporal en la base de datos tempdb, y utilizará esta clave para acceder a las tablas originales, pero en este caso no hay ninguna clave que pueda copiar, el cursor no tiene forma de referenciar a la tabla original. La estrategia que tomará el cursor bajo esta situación es copiar directamente todas las filas a una tabla temporal en tempdb y esta es exactamente la estrategia que utilizan los cursores STATIC (creo esta observación nos servirá de paso para cuestionarnos si los cursores STATIC o KEYSET son la opción más deseable en temas relacionados con la performance). La conclusión de esta prueba es que en ciertas ocasiones un cursor de un tipo determinado podrá modificar su comportamiento dependiendo de otras condiciones, este comportamiento es conocido como conversión implícita de cursor. Más adelante retormaremos este concepto.

Finalmente existe una cuarta opción denominada FAST_FORWARD, la cual creará un cursor de solo avance y solo lectura. Aún no hemos mencionado que significa un cursor de solo avance ni un cursor de solo lectura, pero lo haremos en unos pocos párrafos.

Si nos detenemos en la sentencia FETCH del código que hemos escrito hasta el momento podría parecernos interesante el hecho que la misma se encuentra acompañada de la sentencia NEXT, la cual como hemos visto en los ejemplos devuelve la próxima fila de resultados, pero no es de sorprender que exista además la posibilidad de retroceder el cursor para acceder la fila anterior a la actual. Las sentencias FIRST, PRIOR, NEXT y LAST (en combinación con la sentencia FETCH) perimirán acceder a la primera, previa, próxima y última fila respectivamente. Por otra parte las opciones ABSOLUTE y RELATIVE permitirán avanzar o retroceder una cantidad determinada de registros (en forma absoluta o relativa al registro actual) pudiéndose utilizar, además de constantes, variables del tipo smallint, tinyint o int para indicar la magnitud del desplazamiento. En el siguiente ejemplo (que si bien no parece darle mucha importancia al algoritmo del viajero) se muestran algunas de estas posibilidades:


Cursor Scroll

Para que todas estas opciones de desplazamiento se encuentren disponibles el cursor deberá definirse como SCROLL, que es la opción por defecto, por lo que no es necesario definirlo explícitamente, pero podrá hacerse tal como se muestra en el ejemplo previo.

Otra posibilidad es la de definir el cursor como FORWARD_ONLY. Esta opción, como su nombre nos indica, no permitirá realizar movimientos de retroceso en el cursor, en realidad la opción es aún más restrictiva y solo permitirá efectuar operaciones FETCH NEXT, pero a cambio nos bridará una mucho mejor performance que la opción SCROLL.

41 comentarios »

  1. HMB dice:

    Claro y sencillo… me ayudo.

  2. Rayco dice:

    Una buena iniciación al uso de los cursores en Transact SQL, buen aporte.

  3. Kristian dice:

    Excelente esta muy entendible, gracias por el aporte, pero desearia obtener mas info tal vez me podrian ayudar con un manual. O donde puedo conseguir

  4. andrestkd dice:

    Practico y concreto, buen aporte.

  5. Jose Zarate dice:

    Excelente!! mas claro lo tengo

  6. jeremiasm dice:

    En que parte se guardan los cursores, tengo sql server 2008 y no encuentro en el explorador de objetos el cursor creado.

    • Dario Krapp dice:

      Hola jeremiasm, buenos días

      El tema es que un cursor no es un objeto persistente que se almacena en tablas del SQL como por ejemplo las tablas, los stored procedures o los indices entre otros.
      Los cursores son objetos que se crean en memoria (por eso es importante utilizar la sentencia deallocate para desalojarlo de la memoria que utiliza SQL Server). Por ese motivo no los vas a poder encontrar en el object explorer.
      Slds

  7. Luis dice:

    entendi muy bien, se agradece

  8. Edwin dice:

    Gracias Por la Ayuda …
    Buen Aporte .

  9. Sebastian dice:

    Gracias por el aporte, por cierto que buen nombre el del sitio, me veo muy identificado con este!.

  10. ERNESTO dice:

    Muy buen comentario , quisiera me pudieran ayudar , quiero realizar un cursor haciendo lo siguiente espero me puedan ayudar tengo 2 tablas
    Tabla 1 Folio
    Tabla 2 Folio + Precio

    quisiera actualizar mi tabla 1 de la 2 por medio de un cursor,quedo en espera de sus valiosos comentarios

  11. manuel lora dice:

    excelente me gusto

  12. KS dice:

    muy bueno, pero quisiera que me puedas ayudar, quiero hacer un cursor que me ayude a actualizar un campo por ejemplo:

    telefono DNI1
    telefono DNI1

    de lo anterior quisiera lo siguiente:

    telefono1 DNI1
    telefono2 DNI1

    Tendras algun material que me pueda ayudar.

    Saludos

  13. jrcastillof dice:

    Muy Buen Aporte,, lo voy a revisar, mas tarde (a la media noche), pero por lo que leí se nota claro y sencillo.

  14. VerSanCheZ dice:

    Hola a todos, tengo dos tablas ( Empleados y Entradas ), lo que quiero saber es como le hago para que la consulta me devuelva lo siguiente:

    No. Nombre Reg1 Reg2 Reg3 Reg4
    01 Pedro 07:10:20 11:45:00 12:36:14 18:50:44
    02 Luis 09:15:20 11:05:00 11:36:14 17:50:44

    La Tabla Empleado tienen los siguientes Campos;
    No. int
    Nombre varchar

    Registros de la tabla de Empleados
    01 Pedro
    02 Luis
    03 Paco

    Y la Tabla Entradas
    No. int
    Fecha date
    Registro time(0)

    Registros de la tabla Entradas
    01 2015-01-01 07:10:20
    02 2015-01-01 09:15:20
    01 2015-01-01 11:45:00
    02 2015-01-01 11:05:00
    01 2015-01-01 12:36:14
    02 2015-01-01 11:36:14
    01 2015-01-01 18:50:44
    02 2015-01-01 17:50:44

  15. carlos dice:

    Realmente muy útil!

  16. Jonathan dice:

    Muy bien explicado, excelente (y)

  17. Jesus Ob Ac dice:

    gracias por el aporte, me ha servido como no tienes idea.

    saludos

  18. Roman dice:

    Muchas gracias! Mas claro imposible!!!

  19. Jaqueline Pérez dice:

    Muchas Gracias, es muy util esta información.

    Pero tengo una duda, cree un cursor dentro de un procedimiento almacenado para hacer mas sencilla su ejecución, al ejecutarlo por primera vez no hay problema funciona bien, pero si lo ejecuto por segunda vez no me regresa nada, tengo que esperar para volverlo a ejecutar, a pesar de que tengo el “Dellocate” y el “Close”, ¿Alguna idea de que pudiera pasar?

  20. Jack SK dice:

    Oye, muchas gracias por compartir esta información tan importante de una manera tan clara.

  21. Daniel dice:

    Muy buena explicación!!! Clara y sencilla

  22. Eduardo R. A. dice:

    Hola Dario. Primero gracias por su ayuda.

    Como puedo hacer en el uso de cursores para ver los resultados del proceso de cada registro del cursor. He notado que cuando utilizo cursores los resultados solo se muestran al finalizar el proceso completo del cursor.

    Saludos.
    Nuevamente GRACIAS.

  23. Oscar Ocar's Quiñonez dice:

    Buenísima explicación!!! Me sirvió bastante!

  24. Edgard Huillca dice:

    Como llamo a un cursor dentro de un store

  25. efuv0464 dice:

    Se puede declarar un cursor con la base de datos pasada como parámetro al sp????

  26. kunaguaro007 dice:

    El dia que quiten esta pagina me quedo sin cursores, tengo como 5 años copiando y pegando el codigo jajajajaja

  27. juanmadhardy dice:

    muy bueno, y muy bien escrito.

  28. Sergio Euan dice:

    Muy bueno amigo mio, he revisado este post dos veces y las dos ocasiones me ha servido de mucho. Muchas gracias por el aporte.

  29. Gerardo Chavarria dice:

    Gracias por el aporte, estoy empezando en el uso de cursores

  30. Chava ch dice:

    tengo dudaa

    DECLARE ProdInfo CURSOR FOR SELECT Nombre , precio , cantidadtotal FROM Production.Product

    se puede hacer esto??? el cursor puede guardar mas parametros?

  31. omar dice:

    muy bueno ,siempre ocupe este ejemplo para mis cursores. Gracias

  32. Juan José dice:

    ¡Que pro!

  33. Angel dice:

    Gracias totales la pagina tiene articulos muy interesantes!

Deja un comentario

Este sitio usa Akismet para reducir el spam. Aprende cómo se procesan los datos de tus comentarios.