Gestionar múltiples datasets con Ado y Delphi

Al desarrollar aplicaciones que se conectan a bases de datos nos encontramos con situaciones que nos exigen obtener varios Recordsets para mostrar la información al usuario. Una de las  maneras en que realizamos esta tarea es ejecutando de manera sucesiva, procedimientos almacenados en la base de datos o ejecutando consultas incrustadas en la aplicación.   Esto por supuesto genera el envío repetitivo de información al motor de base de datos lo que conlleva a un mayor tráfico de red y además del trabajo adicional de preparar consultas o procedimientos almacenados con todo lo que ello conlleva.

A la fecha, los motores de bases de datos soportan el envío simultáneo de múltiples Recordsets de información como respuesta a  la ejecución de un procedimiento almacenado, esto también claro puede hacerse a través de la ejecución de varias consultas incrustadas en la aplicación y enviadas en una sola llamada pero en este último caso, el éxito de la llamada dependerá en gran medida del soporte que brinde el driver Odbc o en su efecto OleDb. Es por ello que para este artículo haré uso de procedimientos almacenados con objeto de ejemplificar la manera en que podemos aprovechar esta característica.

Como había mencionado antes, ya que los motores de base de datos nos permiten devolver varios Recordsets como respuesta a la ejecución de un Stored Procedure, es posible aprovechar esta característica para devolver a la aplicación invocante, Recordsets resultantes que se encuentren relacionados,  aprovechando así un solo canal de comunicación. Un ejemplo de Recordsets relacionados sería un maestro detalle de venta, esto es, podríamos tener un procedimiento almacenado cuyo objetivo sea devolver información de una determinada venta, como sabemos, esto en muchas ocasiones nos exige mostrar al usuario encabezado de la venta y detalle de la misma, y algunas veces un detalle adicional como podría ser información extendida del cliente. Esto para realizarlo,  nos exigiría hacer tres distintas llamadas a la base de datos solicitando la información respectiva aún a pesar de tener un filtro en común: El número de venta.  Con el soporte de múltiples Recordsets que ADO nos ofrece, es posible desde Delphi invocar a un Procedimiento almacenado que nos devuelva los tres bloques de información en una sola llamada.  Con esto en mente crearemos nuestro procedimiento almacenado.

Importante Aunque para este ejemplo usaré SQL Server 2000,  lo aquí expuesto aplica para cualquier motor de base de datos que soporte el envió de múltiples Recordsets como respuesta de salida a la invocación de un procedimiento almacenado.

Este es nuestro procedimiento almacenado que devuelve el encabezado de la nota de venta, el detalle de partidas y la información adicional del cliente:

-- =============================================
-- Procedimiento almacenado cuyo objetivo es ejemplificar
-- el manejo de múltiples recordsets desde delphi
-- =============================================

IF EXISTS (SELECT name 
       FROM   sysobjects 
       WHERE  name = N'MultiplesRecordSets' 
       AND    type = 'P')
    DROP PROCEDURE MultiplesRecordSets
GO

CREATE PROCEDURE MultiplesRecordSets
       (@NotaVenta as Integer)  
AS

  Declare  
      @NoCliente as Numeric(6,3);
  Set @NoCliente = rand(100) 
  set @NoCliente =  Cast(@NoCliente * 1000 as Numeric(3,0))

  -- Esta consulta devuelve información de la nota de venta
  Select @NotaVenta NoVenta, GetDate() FechaVenta,  @NoCliente NoCliente,
         'CLIENTE DEMO S.A DE C.V' NOMBRE, 2500 SUBTOTALVENTA

  -- Esta consulta devuelve el detalle
  Select 1 NoPartida, '00001' CveProd, 'ARTÍCULO 0001' [DESC], 1000 PRECIOUNITARIO UNION
  Select 2 NoPartida, '00002' CveProd, 'ARTÍCULO 0002' [DESC], 1000 PRECIOUNITARIO UNION
  Select 3 NoPartida, '00003' CveProd, 'ARTÍCULO 0003' [DESC], 500 PRECIOUNITARIO 

  -- Esta consulta devuelve información del cliente
  select @NoCliente NoCliente, 'CLIENTE DEMO S.A DE C.V' NOMBRE,
         'RFC EMPDEM25497998' RFC, 'CONTACTO DEMO' CONTACTO

GO

Como podemos observar, al ser ejecutado, este  procedimiento almacenado devolverá tres recordsets simultaneos con toda la información que necesitamos mostrar al usuario resposnable de revisar la ventas, o a quién ha ejecutado un reporte. Si ejecutamos el procedimiento almanceado, Sql server nos mostrará el siguiente resultado:

Bueno, ya tenemos el procedimiento almacenado, ahora vamos a ejecutarlo para obtener los tres Recordsets al mismo tiempo. Para ello vamos a abrir nuestro Delphi y Generar una nueva aplicación VCL, colocamos en el formulario principal un AdoConnection, Tres AdoStoredProcedure, Tres DbGrids, Tres Labels y un botón.  Luego, configuramos la cadena de conexión a nuestro motor de base de datos, y enlazamos los grids a los datasources y estos a su vez a los AdoStoredProcedures respectivos. Por último editaremos el evento OnClick del botón y colocaremos el siguiente código:

 

procedure TForm1.Button1Click(Sender: TObject);
var
  LrcsAuxiliar: _Recordset;    //Recibe temporalmente uno a uno los recordsets obtenidos
  LoFilasAfectas: Integer; //Filas afectadas en el recordset
begin
  AdpEncaVta.ProcedureName := 'master.dbo.MultiplesRecordSets';
  AdpEncaVta.Parameters.CreateParameter('@NotaVenta',ftInteger,
                                        pdInput,11,StrtoInt(Edit1.Text));
  AdpEncaVta.Open;

  LrcsAuxiliar := AdpEncaVta.NextRecordset(LoFilasAfectas);
  if Not Assigned(LrcsAuxiliar) Then
    Application.MessageBox('No se pudo encontrar el recordset detalle venta',
                           Pchar(Application.Name),MB_ICONERROR + MB_OK)
  Else
    AdpDetVta.Recordset := LrcsAuxiliar;

  LrcsAuxiliar := AdpEncaVta.NextRecordset(LoFilasAfectas);
  if Not Assigned(LrcsAuxiliar) Then
    Application.MessageBox('No se pudo encontrar el recordset del cliente',
                           Pchar(Application.Name),MB_ICONERROR + MB_OK)
  Else
  AdpInfoCte.Recordset := LrcsAuxiliar;
end;

Y al presionar el botón tendremos el siguiente resultado:

 

Como podemos ver, obtener múltiples Recordsets es muy sencillo con Delphi y ADO. He colocado el código fuente de este artículo en la zona de descargas del foro Delphi Access

http://www.delphiaccess.com/forum/downloads/?cat=11

4 Comments

  1. Hola Edgar.

    Interesante artículo, sin duda. Desconocía esta característica de devolver varios cursores con una misma solicitud a la base de datos. Queda muy clara la utilidad que tienen, sobre todo cuando el tráfico de red es un asunto importante.

    Me despertó curiosidad por saber si Firebird cuenta con esa capacidad de MS SQL Server; no tengo la última versión, pero creo no (supongo que por alguna cuestión de estándares).

    Al menos con dbExpress sí parece posible aprovecharla: según veo la clase TSQLStoredProc viene con un método NextRecordSet cuyo propósito es similar al de ADO.

    Enhorabuena por esta estupenda bitácora, hace ya tiempo que traías la espinita. Me uno a tu lista de lectores. 🙂

    Al González.

    • Saludos amigo…

      Efectivamente, el uso de múltiples recordsets ahorra tráfico en la red y además código en nuestras aplicaciones. Ahora bien, devolver múltiples cursores es una característica de la mayoría de bases de datos, por mi parte la he usado en mysql, db2, mssql, oracle y postgresql. Para firebird sería genial que nos dijeras si lo soporta…

      Saludos¡¡

Deja un comentario