Tipos de cursores de ADO (Avanzado)

INTRODUCCIÓN:


Primero de todo quiero avisar que este artículo no pretende defender ni fomentar el uso de cursores contra viento y marea. Su propósito es el de entender su funcionamiento y los diferentes tipos que podemos usar. Digo esto porque existe un nutrido grupo de usuarios que ‘demonizan’ el concepto de cursor y basan sus aplicaciones en modelos desconectados (tan de moda desde la aparición del fenómeno Internet), de hecho la evolución natural de ADO (ADO.NET) trabaja en un modelo desconectado y no admiten ciertos tipos de cursor:


[…] “ADO también admite algunos comportamientos no expuestos a través de ADO.NET, como cursores de servidor con desplazamiento.” (Tecnologías de acceso a datos – MSDN .NET Framework v1.1)


¿Es esto bueno o malo? Ni una cosa ni otra… pero en ocasiones nos veremos forzados a usar ADO respecto a ADO.NET (Por lo pronto todos aquellos que desarrollemos aplicaciones con VB 6.0 o VBA que no somos pocos!) :-), y conviene saber que tenemos entre manos.


Para esto aclaremos dos aspectos:


Tipos de cursores en ADO: Al definir el tipo del cursor determinaremos la funcionalidad disponible y su comportamiento. Existen cursores que permiten desplazarse en todas las direcciones y otros no, algunos muestran los cambios de otros usuario y otros no, y algunos saben determinar cuántos registros ha devuelto la consulta y… efectivamente, otros no.


        – ForwardOnly (sólo hacia delante)
        – Firehose (manguera de bombero)
        – Static (Estático)

        – KeySet (Conjunto de claves)

        – Dynamic (Dinámico)


Localización del cursor: Define quien se encarga de mantener los datos del cursor, el sistema de base de datos (servidor) o el motor de cursor de ADO (cliente).


        – Cursores de Servidor
        – Cursores de Cliente
        – Prueba de rendimiento






Resumen de tipos de cursores:


El cursor ForwardOnly:


Es el cursor más simple que existe. Sólo permite desplazamiento hacia delante y al abandonar un registro éste deja de estar disponible, esto que a priori podría descartarlo por su aparente sencillez hace que sea el cursor más rápido, y lo hace ideal para todas aquellas operaciones dónde debemos abrir un conjunto de registros e ir haciendo una lectura secuencial hasta llegar al final (El típico ejemplo es para llenar una lista de opciones con los valores de una tabla).


Al abrir un cursor de este tipo se leen un numero de registros y se pasan a la caché (en función del valor de la propiedad CacheSize), y a medida que se va avanzando por el cursor, ADO recupera el conjunto siguiente de registros.



El cursor Firehose:


Existe un tipo de cursor especial de cursor hacia delante de sólo lectura y que devuelve los registros de uno en uno (como si se hubiese especificado un CacheSize=1). Como los registros salen uno a uno y es muy rápido de aquí viene su nombre (os lo juro!).


Muchos proveedores admiten este tipo especial de cursor, de hecho en ADO es el tipo de cursor predeterminado y el propio SQL Server está optimizado para este tipo de cursor.


Eso si, me gustaría hacer una advertencia: este tipo de cursor sólo admite una consulta activa en la conexión, por lo que si ADO recibe otra consulta mientras está ocupado leyendo datos con un cursor de este tipo, en lugar de provocar un error abrirá una segunda conexión (de forma transparente para el usuario) pero este comportamiento puede dar lugar a multitud de dolores de cabeza, sobre todo dentro del contexto de una transacción.


El cursor Static:


Un cursor estático es muy similar a un cursor sólo hacia delante, con la salvedad de que admite desplazamiento en todas las direcciones (MoveFirst, MovePrevious, MoveNext, MoveLast y Move), el propio motor de ADO recuperará en la caché los registros que necesite al desplazarnos por el mismo.


El comportamiento de este cursor no muestra los cambios realizados por otros usuarios a los datos de los registros del cursor, de la misma forma que no muestra los registros añadidos ni eliminados, es por ello que se denomina estático (y en según que proveedores a este cursor se ha denominado Snapshot).


Tradicionalmente estos cursores han sido de sólo lectura pero en ADO pueden ser de lectura/escritura, de hecho todos los cursores del lado cliente (como veremos posteriormente) son de este tipo, pero no nos adelantemos…


El cursor KeySet:


Esto empieza a complicarse, el cursor de conjunto de claves es junto con el dinámico el más complejo de todos los tipos de cursor que veremos. Básicamente su comportamiento permite tanto actualizar los datos como ver los cambios que los otros usuarios puedan hacer en los registros del cursor. Lo que no permite ver son registros añadidos por otros usuarios a la base de datos.


¿Extraño no?, vamos a ver como funciona esto:



El procesador de consultas recupera inicialmente únicamente los datos necesarios para localizar los datos en las tablas que intervienen en la consulta. Generalmente, estos datos corresponden a la clave primaria de las tablas (por eso se llama conjunto de claves). Gracias a la información de la clave, el motor de ADO podrá localizar los datos de estos registros en la base de datos, y además en caso de actualización de este registro la clave posee la información básica para poder actualizar un registro en la base de datos.


Así que resumiendo, el motor de ADO carga únicamente las claves de los registros afectados en la consulta, y a medida que nos vamos desplazando por el conjunto de claves se van cargando en la caché los registros necesarios de la base de datos (como siempre dependiendo del valor de CacheSize). En caso de que alguno sea actualizado el propio motor, a partir del valor de la clave,  se encarga de enviar los comandos necesarios al servidor para actualizar el registro.


Sin embargo hay que tener en cuenta algo importante: El conjunto de claves cargado por el motor de ADO es estático, y claro, si otro usuario inserta un registro nuevo en la base de datos, al no estar cargada su clave, no aparecerá en nuestro conjunto de resultados ¿lógico no?. ¿Y los registros eliminados? A veces provocan comportamientos extraños, ya que tenemos una clave que apunta a un registro que no existe (como decimos habitualmente ‘apunta a Pekín’), de forma que serán eliminados cada vez que se actualice la caché.


El cursor Dynamic:


El cursor dinámico es muy parecido a cursor de conjunto de claves. La diferencia está en que cada vez que el cliente solicita otro conjunto de registros el conjunto de claves se vuelve a crear antes de devolver estos registros. Esto provoca que si abrimos un conjunto de registros dinámico y contamos el número de registros, luego nos desplazamos secuencialmente hasta el final y volvemos a contar el número de registros, no tiene por que ser el mismo ya que otros usuarios pueden haber insertado registros.



Evidentemente, el rendimiento de estos cursores es mucho más bajo contra más prestaciones proporcionan, siendo el de mejor rendimiento el cursor sólo hacia delante y el peor el dinámico. Para hacernos una idea del orden de magnitud al final del artículo he realizado una prueba de rendimiento entre los diferentes tipos






Resumen de localización de cursores:


Cursores de extremos de Servidor:


Los tipos de cursores descritos anteriormente sólo son aplicables a cursores del lado servidor, ya que todos los cursores del lado cliente son estáticos. Los cursores del servidor no son todos iguales, ya que dependiendo del proveedor OLE DB utilizado los datos se mantienen en lugares distintos (Por ejemplo en SQL Server el cursor es mantenido por el servidor, mientras que en Access o en Oracle el proveedor OLE DB o el controlador ODBC tienen mucho más trabajo).


Los cursores de servidor de ADO no son la opción más recomendable en aplicaciones con muchos usuarios concurrentes o con previsión de escalabilidad, ya que a mayor número de usuarios mayor va a ser el trabajo del servidor manteniendo los estados de los registros de los cursores abiertos (que dicho sea de paso, es una forma de desaprovechar la potencia de un servidor de datos).


Según leí una vez en un libro de programación ADO contra SQL Server “La experiencia ha demostrado que al basar una aplicación en cursores de extremo de servidor con SQL Server provoca un rendimiento pobre y dificultados en el escalado, y puede conducir a problemas de calvicie o al paro”.


Cursores de extremo de Cliente:


Como ya hemos comentado, si abrimos un cursor de extremo de cliente y posteriormente comprobamos el valor de la propiedad CursorType, observaremos que estamos utilizando un cursor de tipo estático. Pero.. resulta que el comportamiento de este cursor estático no es exactamente el de un cursor estático, ¿y como es eso? Bueno, como dijo Jack el destripador “…vayamos por partes“:


En un cursor del extremo del cliente, el motor de ADO pasa la consulta al servido y recupera los datos a través de un cursor de tipo manguera de bombero, pero los datos son almacenados en el propio motor de cursor de ADO. Esto provoca que podamos desplazarnos por los datos como en un cursor estático, e incluso que podamos actualizar la información, lo único que no podemos ver son los cambios realizados por otros usuarios.


La pregunta que surge es ¿como podemos actualizar la información de un cursor que no reside en el servidor?, pues resulta que el motor de cursor de ADO, en el momento en que invocamos al método Update o UpdateBatch y basándose en los cambios realizados en los registros, actualiza la base de datos enviando consultas de acción que reflejan estos cambios.



El mecanismo del motor de cursor de ADO que realiza estas consultas es bastante complejo y daría para un artículo tan extenso como este. Tal vez más adelante publique un artículo sobre esto… si tengo tiempo.


😀






Prueba de rendimiento:


Banco de pruebas:


Pentium 4 a 2,8 GHz con 512 Mb RAM


Windows 2000 PRO + SP 4


SQL Server 2000 + SP 3


VB 6.0 + SP 5


MDAC 2.7


Query:


SELECT * FROM Historico (tabla de 22 columnas con clave principal INT IDENTITY, con 567.430 registros)


Abrir un RecordSet y leer secuencialmente (Do-Loop) hasta EOF:






















Tipo de cursor Segundos

ForwardOnly

6,02
Static 65,48
KeySet 90,13
Dynamic 89,84
Static (extremo cliente) 39,00

¿Hace falta algún comentario?


Que esto sirva para aquellas ocasiones en que no nos fijamos demasiado en el tipo de cursor que utilizamos.


🙂

One thought on “Tipos de cursores de ADO (Avanzado)

  1. Felicidades! hermano, que buen artículo. Soy programador con mucha experiencia en SQL (12 años) y te puedo comentar que nunca vi un artículo tan completo y valioso como este.

Leave a Reply

Your email address will not be published. Required fields are marked *