02 octubre 2007

La potencia de los ClientDataSet (y V)

Antes de proceder a crear el código para el botón Completar vamos a hacer que la conexión del programa con la base de datos sea algo más flexible.

Hasta ahora nuestro objeto IBDatabase está conectado directamente a una ruta fija donde tenemos el archivo BASEDATOS.FDB. Pero si cambiamos de ruta el programa dejará de funcionar provocando un fallo de conexión.

Para evitar esto vamos a hacer que el programa conecte con la base de datos al mostrar el formulario principal FClientes. Para ello en el evento OnShow de dicho formulario ponemos lo siguiente:

procedure TFClientes.FormShow( Sender: TObject );
begin
AccesoDatos.BaseDatos.DatabaseName := '127.0.0.1:' + ExtractFilePath( Application.ExeName ) + 'BASEDATOS.FDB';

try
AccesoDatos.BaseDatos.Open;
except
raise;
end;

LogicaNegocio.TLstClientes.Active := True;
end;

Esto hace que conecte con la base de datos que está al lado de nuestro ejecutable. Así hacemos que nuestro programa sea portable (a falta de instalarle el motor de bases de datos correspondiente). Si la conexión con la base de datos es correcta entonces abrimos la tabla del listado de clientes (TLstClientes).

Cuando se trata de una base de datos Interbase la conexión puede ser local o remota. Si es local no es necesario poner la IP:

AccesoDatos.BaseDatos.DatabaseName := 'C:\MiPrograma\BaseDatos.gdb';

Aunque viene a ser lo mismo que hacer esto:

AccesoDatos.BaseDatos.DatabaseName := '127.0.0.1:C:\MiPrograma\BaseDatos.gdb';

Se trata de una conexión remota aunque estemos accediendo a nuestro mismo equipo. Si se tratara de otro equipo de la red habría que hacer lo mismo:

AccesoDatos.BaseDatos.DatabaseName := '192.168.0.1:C:\MiPrograma\BaseDatos.gdb';

Para bases de datos Firebird no existe la conexión local, siempre es remota. Así que si vamos a conectar con nuestro equipo en local habría que hacerlo así:

AccesoDatos.BaseDatos.DatabaseName := '127.0.0.1:C:\MiPrograma\BaseDatos.fdb';

Yo recomiendo utilizar siempre la conexión remota utilizando para ello un archivo INI al lado de nuestro programa que contenga la IP del servidor. Por ejemplo:

[CONEXION]
IP=127.0.0.1

Así podemos hacer que nuestro programa se conecte en local o remoto sin tener que volver a compilarlo. Pero que no se os olvide dejar desconectado el componente IBDatabase en el módulo de acceso a datos AccesoDatos porque si no lo primero que ha a hacer es conectarse al arrancar el programa provocando un error.

DANDO DE ALTA CLIENTES DE FORMA MASIVA

Para probar el rendimiento de un programa no hay nada mejor que darle caña metiendo miles y miles de registros a la base de datos. Para ello voy a crear un procedimiento dentro del botón Completar que le preguntará al usuario cuandos clientes desea crear. Al pulsar Aceptar dará de alta todos esos registros mostrando el progreso en la barra de progreso que pusimos anteriormente:

procedure TFClientes.BCompletarClick( Sender: TObject );
var
sNumero: string;
i, iInventado: Integer;
begin
sNumero := InputBox( 'Nº de clientes', 'Rellenando clientes', '' );
Randomize; // Inicializamos el generador de números aleatorios

if sNumero <> '' then
begin
with LogicaNegocio do
begin
TClientes.Open;
Progreso.Max := StrToInt( sNumero );
Progreso.Visible := True;
TLstClientes.DisableControls;

for i := 1 to StrToInt( sNumero ) do
begin
iInventado := Random( 99999999 ) + 1000000; // Nos invertamos un número identificador de cliente
TClientes.Insert;
TClientesNOMBRE.AsString := 'CLIENTE Nº ' + IntToStr( iInventado );
TClientesNIF.AsString := IntToStr( iInventado );
TClientesDIRECCION.AsString := 'CALLE Nº ' + IntToStr( iInventado );
TClientesPOBLACION.AsString := 'POBLACION Nº ' + IntToStr( iInventado );
TClientesPROVINCIA.AsString := 'PROVINCIA Nº ' + IntToStr( iInventado );
iInventado := Random( 79999 ) + 10000; // Nos inventamos el código postal
TClientesCP.AsString := IntToStr( iInventado );
TClientesIMPORTEPTE.AsFloat := 0;
TClientes.Post;
Progreso.Position := i;
Application.ProcessMessages;
end;

TClientes.Close;
Progreso.Visible := False;
TLstClientes.EnableControls;
end;
end;
end;

Lo que hemos hecho es inventar el nombre de usuario, dirección, NIF, etc. También he desconectado y he vuelto a conectar la tabla TLstClientes de la rejilla utilizando los métodos DisableControls y EnableControls para ganar más velocidad. La barra de progreso llamada Progreso mostrará la evolución del alta de registros.

Realmente es una burrada dar de alta masivamente registros utilizando objetos ClientDataSet ya que están pensados para utilizarlos para altas, modificaciones y eliminación de registros de uno a uno y sin mucha velocidad. Si quereis hacer una inserción masiva de registros hay que realizar consultas SQL con INSERT utilizando el objetos de la clase TIBSQL que es mucho más rápido que los ClientDataSet. Ya explicaré en otro momento la ventaja de utilizar dichos componentes.

Con esto finalizamos la introducción a los componentes ClientDataSet.

Pruebas realizadas en Firebird 2.0 y Delphi 7.

3 comentarios:

Pablo Zagni dijo...

Fue excelente y muy claro este artículo. Por fin pude entender BIEN lo que son los ClientDataSet y su funcionamiento.

Ahora bien, estoy teniendo problema con los ClientDataSet anidados. Piensas sacar algún post sobre este tema?

Saludos
Pablo Zagni
Argentina

Ariel Macias dijo...

Muy buena la forma como tratas los distintos temas...
Te hago una consulta con respecto a esto de los ClientDataSet
Necesito una guia: por ejemplo como se haria si quiero filtrar ahora por nombre del cliente

SELECT * FROM CLIENTES
WHERE NOMBRE=:NOMBRE

Es para un caso parecido, resulta que tengo un pequeño sistemita echo de farmacia, donde tengo una tabla medicamentos, esta tabla tiene un id y un nombre mas otros campos como precio, presentacion, etc
Es para hacer una especie de factura al momento de ir agregando varios medicamentos, nadie se acuerda de los id o codigo del medicamento, pero si se puede buscar por el nombre del mismo, entonces necesitaria, tener una caja de texto (Edit) en donde al ir ingresando letras, se me vaya filtrando en una grilla los mismos... espero que me hayas entendido lo que quiero hacer

Administrador dijo...

Si la base de medicamentos no es muy extensa (no tiene cientos de miles de registros) encontes puedes utilizar la propiedad Filter del componente TClientDataSet (llamado Medicamentos) del siguiente modo:

Supongamos que tienes el componente TEdit llamado Nombre y en el evento OnChange:

if Nombre.Text = '' then
Medicamentos.Filtered := False
else
begin
Medicamentos.Filter := 'NOMBRE LIKE ' + #39 + '%' + Nombre.Text + '%' + #39;
Medicamentos.Filtered := True;
end;

De este modo, conforme vayas tecleando, se irán filtrando en tiempo real los nombres de los medicamentos que contengan la cadena que has escrito (al estilo Google).

Si fueran muchos registros, también lo puedes hacer filtrando directamente en el TIBQuery que tienes vinculado al ClientDataSet modificando directamente su SQL cerrándolo, modificando su SQL y abriéndolo de nuevo pero dejando su transacción abierta.

De todas formas, creo que con el Filter del ClientDataSet te va a ir bastante bien.

Saludos.

Publicidad