Página principal
Artículos y trucos
Catálogo de productos
Ejemplos y descargas
Mis libros
Cursos de formación
Investigación y desarrollo
Libros recomendados
Mis páginas favoritas
Acerca del autor
 
En colaboración con Amazon
 
Intuitive Sight

El controlador MSDataShape

Antes de comenzar con el artículo en sí, quiero aclarar que al escribir sobre MSDataShape no estoy recomendado su uso, al menos en estos momentos: mientras preparaba el artículo, he encontrado un par de violaciones de acceso, aunque tienen todo el aspecto de ser achacables a Delphi. Pero veréis que se trata de un enfoque "fresco" e interesante al problema de la actualizaciones en caché, y puede que más adelante los problemas se resuelvan. O incluso puede que esta solución inspire el desarrollo de alternativas.

¿QUE ES EL CONTROLADOR MSDATASHAPE?

MSDataShape es un controlador OLE DB que acompaña a ADO, y su particularidad consiste en que su origen de datos es la salida de otro controlador OLE DB arbitrario, no una base de datos real. Su objetivo es preprocesar algunas instrucciones que ahora explicaré, y en "dar forma" a ciertos conjuntos de registros devueltos por el controlador básico.

Las instrucciones especiales son una extensión de SQL propuesta por Microsoft. Esta es una posibilidad:

SHAPE   {select * from clientes}
APPEND ({select * from direcciones}
        RELATE IDCliente TO IDCliente)

Cuando recibe esta instrucción, MSDataShape pide al proveedor intermedio que recupere todos los registros de clientes, y todos los registros de direcciones por separado. Entonces organiza los registros de manera que junto a cada registro de clientes vayan "físicamente" asociados las direcciones registradas para ese cliente. Con "físicamente" quiero decir que el controlador añade un campo adicional a la consulta de clientes, de tipo adChapter. Este tipo es una colección de registros: precisamente, los registros de direcciones de cada cliente.

Si usted ha trabajado con DataSnap, adChapter le resultará familiar: de hecho, un campo de tipo adChapter corresponde en Delphi a un TDataSetField. Sin embargo, la sentencia anterior no hace exactamente lo mismo que DataSnap cuando se conecta un proveedor a una relación maestro/detalles. Ese caso es más similar a la siguiente instrucción:

SHAPE   {select * from clientes}
APPEND ({select * from direcciones
         where  IDCliente = ?}
        RELATE IDCliente TO PARAMETER 0)
  1. En este último caso, MSDataShape obtiene primero todos los clientes. Luego va recorriendo los clientes, y para cada uno de ellos obtiene las direcciones asociadas por separado. Como resultado, si hay muchos clientes, se realizan muchos más "viajes" al servidor porque se ejecutan más consultas. Tenga en cuenta que así es precisamente como funciona DataSnap.
  2. En cambio, en el primer caso, se realizan solamente dos viajes al servidor, y hay sólo dos consultas en total. Para lograr lo mismo en DataSnap tendríamos que usar dos proveedores independientes, traer sus contenidos por separado a dos TClientDataSet, y establecer la relación maestro/detalles sobre estos dos últimos componentes.

En términos generales, cuando hay que traer muchos clientes, es mejor utilizar el primer sistema. Cuando, por el contrario, se trata de leer un único cliente con todos sus detalles para su modificación, es mejor usar el segundo sistema.

Pero hay más. Observe esta otra instrucción, reconocida por MSDataShape:

SHAPE  {select * from pedidos} AS Pedidos
COMPUTE Pedidos, SUM(Total) AS SubTotal
     BY FormaPago

Ahora MSDataShape trabaja sobre el conjunto de pedidos. Supongamos que no existe una tabla de formas de pago. Como resultado, se genera al vuelo un conjunto de registros maestros, que contiene los siguientes campos:

  1. Pedidos, de tipo adChapter. Aquí se acumulan registros de pedidos para una forma de pago dada.
  2. SubTotal, con el total de los importes para una forma de pago.
  3. FormaPago, que contiene un nombre de forma de pago.

Cualquier parecido con DataSnap es bastante remoto.

¿PORQUE EL INTERES?

¿Por qué me entusiasma tanto el uso de MSDataShape? La culpa la tienen los fallos en los mecanismos de actualizaciones en caché que aquejan al BDE, al propio ADO, y a InterBase Express. En todas estas interfaces de acceso se producen errores cuando se activan las actualizaciones en caché sobre relaciones maestro/detalles. Todos ellos trabajan con cachés independientes para cada conjunto de datos involucrado, y eso propicia que cuando se produce un error en la grabación de un detalle, se haga casi imposible repetir la grabación del registro maestro, porque casi siempre la caché maestra se marca como "limpia". La forma de solucionar estos problemas consiste en no permitir que ninguno de esos sistemas se ocupen de la grabación de cambios, y añadir DataSnap en sustitución del sistema de caché. En DataSnap, la caché de un conjunto de datos con columnas de tipo TDataSetField considera los registros maestros y los de detalles como una misma entidad, y no se producen los problemas mencionados.

Algo similar ocurre con las consultas sobre MSDataShape: si activamos las actualizaciones en caché de ADO (batch updates) se crea una caché integrada, y se evitan los problemas en las grabaciones maestro/detalles.

CONFIGURACION DE CONEXIONES A MSDATASHAPE

Para ser sinceros, conocía este controlador desde hace bastante tiempo, pero un pequeño detalle me impedía hacer experimentos "serios" con él: si me dejaba guiar por el asistente para la creación de conexiones de ADO, solamente podía configurar MSDataShape para que trabajase sobre fuentes de datos ODBC, a través del proveedor OLE DB para ODBC. Como comprenderá, eso limitaba mucho la utilidad práctica de la técnica: ¿para qué usar ODBC si el sistema de base de datos tiene un controlador nativo OLE DB, como es el caso de SQL Server? Escribo el artículo, sin embargo, porque ya sé cómo utilizar cualquier otro proveedor de datos base.

Vamos a crear una conexión a SQL Server para mostrar los parámetros de conexión de MSDataShape; después explicaré cómo hacerlo para Access. Traiga un TADOConnection a un formulario o un módulo de datos. Haga doble clic sobre él, y aparecerá el conocido asistente, que le preguntará si desea utilizar un fichero UDF o construir una cadena de conexión. Elija esta última opción, y pulse el botón Build. En la primera página del diálogo que aparece, hay que seleccionar el controlador MSDataShape:


Y aquí comienzan los problemas, porque cuando pase a la siguiente página, verá que se intenta que indiquemos un DSN de ODBC, para ser utilizado como origen de datos; fue en este punto donde estuve atascado un tiempo.

La solución es sencilla, sin embargo. El problema es que el tipo del proveedor que se utiliza para el acceso a datos viene determinado por un propiedad dinámica del objeto de conexión. Seleccione la última página del asistente (All), y compruebe que la propiedad Data Provider tiene por omisión el valor MSDASQL... que es el nombre del controlador de OLE DB para ODBC. Por lo tanto, si queremos trabajar con SQL Server, debemos hacer doble clic sobre esa propiedad, y cambiar su valor a SQLOLEDB.1, que es el controlador de OLE DB para SQL Server:


Por el contrario, si quisiera utilizar Access, sería preferible indicar Microsoft.Jet.OLEDB.4.0, que es el controlador OLE DB que se monta sobre el motor Jet.

Supongamos que hemos seleccionado SQL Server. A continuación, debemos indicar con qué servidor y qué base de datos vamos a trabajar. Sin embargo, la segunda página sigue teniendo los controles adecuados para una conexión ODBC. Lo que hay que saber aquí es que, cuando se trabaja con el proveedor OLE DB de SQL Server, el parámetro Data Source indica el nombre del servidor, y que Initial Catalog es el nombre de la base de datos dentro de ese servidor. Por eso, en la siguiente imagen, he tecleado christine en el primer apartado, porque es el nombre de uno de mis servidores, y en el punto 3 he especificado que el catálogo inicial es pubs, una de las bases de datos de ejemplo que viene con SQL Server:


Y ya está lista la configuración. Si lo desea, puede pulsar el botón Test connection para comprobar que todo ha ido bien.

CONFIGURACION DE CONEXIONES A MSDATASHAPE

No estaría bien terminar sin poner un ejemplo de consulta. Para que le valga la sentencia SQL si está utilizando Access o SQL Server, voy a suponer que la conexión hace referencia a la base de datos Northwind, común a ambos sistemas. Una vez que tenga preparada la conexión a MSDataShape, traiga un TADOQuery al módulo de datos o formulario, enlácelo a la conexión, y teclee la siguiente instrucción dentro de su propiedad SQL:

SHAPE   TABLE Employees
APPEND (TABLE Orders RELATE EmployeeID TO EmployeeID)

Como puede ver, he aprovechado para presentar algunas variaciones sintácticas. En este caso he sustituido ambas consultas por una cláusula TABLE; la otra posibilidad sintáctica sería utilizar comandos SHAPE anidados.

Añada entonces todos los componentes de acceso a campos de la consulta. Comprobará que aparece un campo llamado Chapter1, para el que Delphi crea un componente llamado ADOQuery1Chapter1, de tipo TDataSetField. Para poder trabajar directamente con las filas de pedidos, sin necesidad de pasar por el campo anidado, traemos un TADODataSet. Sólo necesitaremos modificar una sola propiedad: DataSetField, en la que naturalmente asignaremos un puntero a ADOQuery1Chapter1 (¡no hace falta especificar ni siquiera la conexión).


A partir de ese momento, podremos crear los componentes de acceso a los campos anidados en ADODataSet1. Tome nota de que las propiedades Active de ambos conjuntos de datos quedan también sincronizadas.