Está en la página 1de 28

IBX

Interbase

José Alonso
pepealonso@supercable.es

Cedida su publicación a el Rinconcito de Delphi

No está autorizada su reproducción sin permiso expreso de su autor


Autor: José Alonso - pepealonso@supercable.es

IBX. Conexión directa a Interbase


Con los componentes IBX, podemos tener una
conexión directa con una base de datos
Interbase.

Hasta ahora, cuando nuestras aplicaciones necesitaban acceder a una fuente de datos, era
necesario usar un “intermediario” que nos hiciese el trabajo. Para esto disponíamos del BDE,
ODBC o ADO. Estas capas intermedias entre nuestro programa y los datos, han de ser
distribuidas por nosotros ya que deben estar presentes en las máquinas donde queramos hacer
correr nuestra aplicación. A cambio, nos ofrecen la posibilidad de soluciones genéricas.
Con la versión 5 de Delphi, una nueva pestaña nos ofrece la posibilidad de una relación mas
estrecha entre nuestra aplicación y los datos... siempre que nuestro soporte de datos sea
Interbase. En la pestaña Interbase Express, podemos encontrar los nuevos componentes.
Estos componentes usan directamente la API de Interbase lo que hace que su rendimiento
sea muy superior a los componentes basados en el BDE.

La relación que mantendrá nuestra aplicación con Interbase, será la de estar continuamente
haciendo peticiones de datos al servidor y “escuchando” las respuestas a estas peticiones.
¿Cómo solicitamos datos a Interbase?. Mediante el uso de los componentes (en este caso los
IBX).

En este artículo no trataré los componentes TIBTable,TIBQuery,TIBStoredProc ni


TIBUpdateSQL, por considerar que están solamente para ofrecer compatibilidad y permitir una
migración menos traumática a las aplicaciones basadas en el BDE. Aunque si me permitís una
opinión, nunca he considerado buena idea lo de los cambios a medias. Casi siempre lo que se
empieza a medias, termina a medias.

El componente TIBDatabase.
Este componente es el que nos facilita la conexión con la base de datos. Para ello, solo
hemos de indicarle la ruta completa y el nombre de nuestra base de datos en la propiedad
DatabaseName del componente
Es posible conectarse a bases de datos locales, es decir, situadas en nuestra máquina o a
bases de datos situadas en máquinas remotas.
En el caso de una base de datos local, habremos de especificar la unidad, directorio y
nombre de la base de datos.

IBDatabase1.DataBaseName := ‘C:\Contabilidad\Datos\Gestion.gdb’

En el caso de que la base de datos se encuentre en una máquina remota, la forma de


conectarse a ella, variara dependiendo del protocolo de comunicación que utilicemos.
Por ejemplo, si usamos el protocolo TCP/IP la forma típica de conectarnos sería:

IBDatabase1.DatabaseName := ‘Morgan:C:\Contabilidad\Datos\Gestion.gdb’

El Rinconcito de Delphi 1
Pero TIBDatabase, no solo se ocupa de la conexión con la base de datos. También
encontraremos propiedades y métodos que nos ofrecen información de la conexión y de la
propia base de datos:

Si queremos saber si la conexión con el servidor se ha establecido, podemos usar la función


TestConnected que nos devolverá True si la conexión se ha efectuado y False en caso
contrario.

IBDatabase1.Connected := True;
If IBDatabase1.TestConnected then
ShowMessage(‘La conexión se ha establecido con éxito’)
Else
ShowMessage(‘Fallo al intentar conectar con ‘ + IBDatabase1.DatabaseName);

Podemos especificar el tiempo que queremos que dure la conexión con la base de datos
mediante la propiedad IdleTimer, la cual admite un valor entero que expresa la cantidad de
milisegundos que durará la conexión o cero para que la conexión sea permanente.

Antes de establecer la conexión con la base de datos, tenemos que definir los parámetros
que queremos pasar al servidor. Mediante la propiedad Params podemos hacerlo.

IBDatabase1.Params.Values[‘user_name’] := ‘SYSDBA’;
IBDatabase1.Params.Values[‘password’] := ‘masterkey’;

Todas estas propiedades se pueden asignar en tiempo de diseño de una forma cómoda, con
el editor de propiedades al cual se puede acceder mediante el botón derecho del ratón y
escogiendo la opción Database Editor. La figura 1, muestra el aspecto de este editor.

Figura 1. La ventana de configuración de TIBDataBase.

GetTableNames(List: Tstrings; SystemTables: Boolean); Nos devuelve en el argumento


List una lista con todas las tablas que contiene la base de datos. El argumento SystemTables

El Rinconcito de Delphi 2
indica si queremos que nos devuelva además las tablas de sistema. Por defecto el valor es False
lo que hace que no se incluyan.
GetFieldNames(const TableName: string; List: TStrings); Nos devuelve en el argumento
List una lista con el nombre de todos los campos que integran una tabla determinada que
indicaremos en el argumento TableName.

El componente TIBTransaction.
Una transacción se puede definir como un conjunto de operaciones que deben ser tomadas
como una unidad. Todas las operaciones que realicemos en la base de datos, la definición y
manipulación de datos, trabajan en el contexto de una o mas transacciones.
Una transacción nos ofrece una “foto” del estado de la base de datos y en un Gestor de
bases de datos multigeneracional como es Interbase, el control de las transacciones cobra una
importancia vital.
A diferencia del BDE en la que no es obligatorio el uso de explícito de transacciones, con
IBX es necesario manejar las transacciones, ya que de otra forma, no podremos acceder a las
tablas de la base de datos. Para ello contamos con el componente TIBTransaction. Con este
componente, podemos controlar todo lo relativo a las transacciones.
Como ya he dicho, en una aplicación es obligatorio que haya al menos una transacción,
aunque lo normal es que usemos varias, dependiendo de las necesidades y requerimientos del
caso. Es posible asignar una transacción a un componente TIBDatabase de manera que este sea
esta la transacción por defecto que se usará, de no indicar otra cosa, al asociar cualquiera de los
componentes de manipulación de datos al TIBDatabase. Para ello, el TIBDatabase cuenta con
una propiedad: DefaultTransaction donde podremos indicar cual será la transacción por
defecto que usaremos.
Así mismo, TIBTransaction cuenta con una propiedad DefaultDataBase, donde
asignaremos el TIBDatabase que usara por defecto el componente.

Sin duda, la operación a la que habremos de prestar más atención a la hora de configurar el
componente TIBTransaction será la propiedad Params.
Para facilitarnos la tarea de configurar correctamente la transacción, podemos contar con el
editor que vemos en la figura 2 y que como siempre, se obtiene a través de pulsar el botón
derecho del ratón sobre el componente.

Figura 2. La ventana de configuración de TIBTransaction.

Escapa al objetivo de este artículo explicar con detenimiento los distintos niveles de
aislamiento que representa cada una de las opciones que se nos presentan. Si os sirve de algo mi
experiencia, la mayoría de las transacciones que manejo en mis aplicaciones las configuro como
“Read Commited”. Este nivel de aislamiento permite que se seleccionen, inserten, actualicen y
borren datos de transacciones ya confirmadas y es suficiente para la mayoría de las ocasiones.

El Rinconcito de Delphi 3
Típicamente, el trabajo con un componente TIBTransaction, se limitará a iniciarla y a
confirmar o descartar los cambios que se hayan efectuado en su contexto. Para esto se vale de
los siguientes métodos:

StartTransaction da comienzo a la transacción. Una vez llamado este método, todas las
operaciones que se realicen en la base de datos, habrán de confirmarse o cancelarse invocando a
los siguientes métodos.
Commit termina la transacción confirmando los cambios que se hayan producido. La
transacción se cierra y se liberan los recursos.
Rollback termina la transacción descartando los cambios que se hayan producido. La
transacción se cierra y se liberan los recursos.

Como habrán observado, tanto Commit como Rollback hacen que las conexiones con las
tablas se cierren, con lo que una vez invocados estos métodos habría que volver a abrir la
transacción y restablecer las conexiones con las tablas para que pudiésemos ver los datos.
Si deseamos confirmar o cancelar los cambios, sin que la conexión con la transacción se
cierre, podemos usar los métodos CommitRetaining o RollbackRetaining que hacen lo
mismo que los métodos anteriores, pero sin perderse la conexión con los datos.

El uso típico de estos métodos se asemejaría a lo siguiente:

Procedure TData.DataCreate( Sender: Tobject);


Begin
// Al crearse el Datamodule, iniciamos la transacción
IbTransaction1.StartTransaction;
End;

Procedure Tdata.DataDestroy(Sender: Tobject);


Begin
// Al cerrar el Datamodule, cerramos la transacción... Si está abierta
if IBTransaction1.InTransaction then
IBTransaction1.Commit;
End;

Procedure TData.UnaTablaAfterPost(Sender: DataSet);


Begin
// Confirmamos los cambios y continuamos
try
IBTransaction1.CommitRetaining
Except // Si hay errores, descartamos y seguimos
IBTransaction1.RollbackRetaining
End;
End;

TIBDataSet
Este componente será el que centre gran parte de nuestra atención, ya que será el que
usemos preferentemente en nuestra aplicación. Con él, podremos acceder a una tabla o vista de
nuestra base de datos y realizar las operaciones típicas de mantenimiento de la misma.
Como descendiente de la clase TDataSet, mantiene con esta propiedades y métodos que son
muy comunes, por lo que no se entrará a explicarlos. En la figura 3 puede ver el aspecto del
Object Inspector apuntando a un TIBDataSet.

El Rinconcito de Delphi 4
Figura 3. El inpector de objetos mostrando las propiedades de TIBDataSet.

Hay dos propiedades que son de asignación obligatoria si queremos trabajar con un
TIBDataSet: Database y Transaction. Si al componente TIBDatabase le asignamos en su
momento un valor a la propiedad DefaultTransaction, será este valor el que aparezca
automáticamente en la propie dad Transaction del TIBDataSet.

Una de las quejas más comunes que se hace al BDE, consiste en la cantidad de
instrucciones que lanza cuando nosotros le hacemos un requerimiento de datos. Cualquiera
puede verlo con el SQLMonitor. Una simple operación de apertura de una tabla, genera un buen
puñado de instrucciones. En cambio, con TIBDataSet tenemos la oportunidad de decirle
exactamente que instrucciones debe lanzar para las distintas operaciones. Para ello disponemos
de cinco propiedades:

InsertSQL. Almacena la instrucción SQL válida para insertar una o varias filas en la tabla.
DeleteSQL. Almacena la instrucción SQL válida para borrar una o varias filas de la tabla.
ModifySQL. Almacena la instrucción SQL válida para modificar una o varias filas de la
tabla.
RefreshSQL. Almacena la instrucción SQL válida para releer la información de una o
varias filas.
SelectSQL. Almacena la instrucción SQL válida para obtener un conjunto de datos de la
tabla.

El componente dispone de unos asistentes que nos facilitan la introducción de estas

El Rinconcito de Delphi 5
sentencias de una forma visual. Para acceder a ellos solo hay que hacer clic con el botón
derecho del ratón y escoger la opción adecuada. Ver figuras 4 y 5.

Figura 4. Construyendo visualmente la sentencia SELEC de un TIBDataSet.

Figura 5. Construyendo visualmente las sentencias Insert, Update y Delete de un TIBDataSet.

El Rinconcito de Delphi 6
Este componente, sus propiedades y métodos y sus eventos, requieren que nos demos un
respiro, tomemos aire y lo abordemos con la suficiente profundidad en un próximo artículo.
Pero para abrir boca, sugiero al amable lector que mire el ejemplo que acompaña a este artículo
y compruebe la potencia que nos proporciona este componente.
Se trata de una pequeña aplicación cuyo objetivo es rescatar filas de una tabla. Las
condiciones que deban cumplir las filas son totalmente configurables. Se trata de un ejemplo de
cómo manejar la propiedad SelectSQL de un TIBDataSet en tiempo de ejecución, amén del
uso de ciertos métodos de TIBDatabase.

El Rinconcito de Delphi 7
Trabajando con TIBDaSet.Se verá como se puede configurar el componente, paso de parámetros,
etc para las operaciones de mantenimiento de datos.

En la anterior entrega de esta serie, dimos un repaso a todos los componentes de la paleta de Interbase.
Vimos como configurar un TIBDatabase, como controlar las transacciones con TIBTransaction y alguna de
las propiedades mas usuales de TIBDataSet.
Deciamos en esa ocasión que en una futura entrega, se veria mas extensamente este ultimo componente,
que sin duda centrará el 99% del trabajo con datos en nuestra aplicación. Como lo prometido es deuda, aquí
va esta segunda entrega.

TIBDataSet. El centro de operaciones

Definición de los datos


Para una mejor comprensión y en el convencimiento de que un ejemplo vale más que mil explicaciones,
dedicaré el artículo a desarrollar una pequeña aplicación de entrada de artículos y facturas (¡que original!)
Una advertencia. Si quereis probar el ejemplo que acompaña al artículo y teneis la versión 6 de
Interbase, habreis de recrear la base de datos ejecutando el script que se acompaña, ya que la base de datos
está creada con Interbase 5.6. Tambien será bueno señalar que la versión de los componentes con la que se
ha desarrollado el ejemplo, es la versión 4,52. Podeis averiguar la versión que teneis instalada, accediendo al
menú contextual de cualquiera de los componentes de la paleta Interbase. Si los vuestros no están
actualizados, podeis descargar la actualización de la página de Borland.
Bien. Metámonos en faena. Lo primero que haremos será definir la base de datos contra la que
trabajaremos. En el Listado 1, podemos ver la parte del script donde se definen las tablas y sus tipos de
datos.
Listado 1.

/*Definición de dominios*/
CREATE DOMAIN CAR_10 AS VARCHAR(10) CHARACTER SET NONE COLLATE NONE;
CREATE DOMAIN CAR_25 AS VARCHAR(25) CHARACTER SET NONE COLLATE NONE;
CREATE DOMAIN CAR_5 AS VARCHAR(5) CHARACTER SET NONE COLLATE NONE;
CREATE DOMAIN CODIGOS_CHAR AS VARCHAR(15) CHARACTER SET NONE default "" NOT NULL COLLATE
NONE;
CREATE DOMAIN EJERCICIOS AS VARCHAR(4) CHARACTER SET NONE default "" NOT NULL COLLATE NONE;
CREATE DOMAIN FECHA_AHORA AS DATE default "NOW" NOT NULL;
CREATE DOMAIN LOGICO AS SMALLINT default 0 NOT NULL check(value in(0,1));
CREATE DOMAIN MONEDA AS DOUBLE PRECISION default 0 NOT NULL;
CREATE DOMAIN NOMBRES AS VARCHAR(60) CHARACTER SET NONE COLLATE NONE;
CREATE DOMAIN NUMERO_CORTO AS SMALLINT default 0 NOT NULL;
CREATE DOMAIN NUMERO_LARGO AS INTEGER default 0 NOT NULL;

/*Definición de generadores*/
CREATE GENERATOR CLIENTES;
CREATE GENERATOR FACTURAS;

/*Definición de excepciones*/
CREATE EXCEPTION ART_NO_CODIGO "NO HA INDICADO EL CODIGO DEL ARTICULO";
CREATE EXCEPTION CLI_NO_NOMBRE "NO HA INDICADO EL NOMBRE DEL CLIENTE";
CREATE EXCEPTION ERR_NO_HAY_EJERCICIO_ACTIVO "No se puede dar un número de factura. No hay
ejercicio activo";
CREATE EXCEPTION FAC_NO_NUMERO "No se ha indicado el numero de factura";

El Rinconcito de Delphi 8
/*Definición de tablas*/
/*Tabla: ARTICULOS, Owner: SYSDBA*/
CREATE TABLE ARTICULOS (CODIGO CODIGOS_CHAR,
DESCRIPCION NOMBRES,
EXISTENCIAS NUMERO_LARGO,
PRECIO_VENTA MONEDA,
ACTIVO LOGICO,
CONSTRAINT ARTICULOS_PK PRIMARY KEY (CODIGO));

/*Tabla: CLIENTES, Owner: SYSDBA*/


CREATE TABLE CLIENTES (CODIGO NUMERO_LARGO,
NOMBRE NOMBRES,
NIF CAR_10,
DIRECCION NOMBRES,
C_POSTAL CAR_5,
LOCALIDAD CAR_25,
PROVINCIA CAR_25,
IVA MONEDA,
ACTIVO LOGICO,
CONSTRAINT CLIENTES_PK PRIMARY KEY (CODIGO));

/*Tabla: CONTADORES, Owner: SYSDBA*/


CREATE TABLE CONTADORES (EJERCICIO EJERCICIOS,
FACTURAS NUMERO_LARGO,
ACTIVO LOGICO,
CONSTRAINT CONTADORES_PK PRIMARY KEY (EJERCICIO));

/*Tabla: FACTURAS, Owner: SYSDBA*/


CREATE TABLE FACTURAS (CODIGO NUMERO_LARGO,
NUMERO NUMERO_LARGO,
EJERCICIO EJERCICIOS,
CLIENTE NUMERO_LARGO,
FECHA FECHA_AHORA,
IVA MONEDA,
BASE_IMP MONEDA,
TOTAL_IVA MONEDA,
IMPORTE_TOTAL MONEDA,
CONSTRAINT FACTURAS_PK PRIMARY KEY (CODIGO));

/*Tabla: FAC_DETALLES, Owner: SYSDBA*/


CREATE TABLE FAC_DETALLES (LINEA NUMERO_CORTO,
FACTURA NUMERO_LARGO,
ARTICULO CODIGOS_CHAR,
DESCRIPCION NOMBRES,
P_UNITARIO MONEDA,
CANTIDAD NUMERO_LARGO,
TOTAL_LINEA MONEDA,
CONSTRAINT FAC_DETALLES PRIMARY KEY (FACTURA,LINEA));

Una vez desarrolada la base de datos, pasemos a lo que es el objeto de este artículo: El trabajo con los
IBX.

El módulo de datos principal


Creamos un módulo de datos donde situaremos un TIBDatabase y un TIBTransaction. Este módulo de
datos se crea con la aplicación y se destruye cuando esta finaliza. Configuramos debidamente ambos
componentes para que podamos establecer la conexión con la base de datos. Para ello, basta con que
señalemos en la propiedad DatabaseName del IBDatabase la ruta completa donde se encuentra nuestra base
de datos e indicar en DefaultTransaction la transacción por defecto.

El Rinconcito de Delphi 9
Si se observa el código, aprovechamos el evento OnCreate del DataModule, para establecer la conexión
con la base de datos e iniciar la transacción.
procedure TDMMain.DataModuleCreate(Sender: TObject);
begin
try
// ¡Conectamos!
DB.Connected := True;
// TestConnected, devuelve True si se ha podido conectar.
// Aprovechamos para iniciar la transacción
if DB.TestConnected then
TPermanente.StartTransaction;
except
ShowMessage('No se ha podido establecer la conexión con la base de datos');
// veamos el error que nos dá
raise
// Y terminamos
Application.Terminate;
end;
end;

// Al destruise el módulo de datos, cerramos la transacción


procedure TDMMain.DataModuleDestroy(Sender: TObject);
begin
TPermanente.Commit;
end;

Poco mas necesitaremos en este módulo. Solo indicar que la transacción la he configurado con un nivel
de aislamiento READ COMMITED, ya que este nivel posibilita que la transacción vea todos los datos
confirmados en la base de datos y actualice filas confirmadas por otras transacciones simultáneas.
Los demás módulos de datos del ejemplo, se crean y destruyen conforme nos van siendo necesarios.
Sobre ellos no haré mas comentarios, pues creo que con una mirada al código es mas que suficiente para
entender lo que hace.

Facturas, facturas y más facturas.


No, no preocuparos, que no os voy a aburrir con mi estado financiero. ¡Este artículo podría convertirse
en un valle de lágrimas!. Mi intención es hablaros del módulo de datos que gestiona todo lo realcionado con
los datos de las facturas y sus líneas de detalle.
Tenemos dos TIBDataSet (IBFacturas e IBDetalles) unidos con una relación maestro-detalle. La
forma de conseguir esto es llenando la propiedad DataSource (por cierto, que no es el mejor nombre que
podian haberle puesto) de IBDetalles con el DataSource relacionado con IBFacturas . Además, hay que
añadir a la sentencia SelectSQL de IBDetalles, la clausula WHERE FACTURA=:CODIGO. El
parámetro CODIGO, tiene el mismo nombre que el campo de la tabla de facturas por el que se relacionan.
En estas circunstancias, el componente se encarga de sustituir el parámetro, por el valor del campo del mismo
nombre de la tabla maestra.

Hay dos IBDataSet mas, xClientes y xArticulos, que solo se usan a efectos de visualización de
datos. El primero de ellos, para mostrar los datos relacionados con el cliente al que se le hace la factura y el
segundo, para los de los artículos.
En este módulo de datos, tambien se han incluido cuatro TIBSql que nos servirán para acceder a los
Stored Procedures de la base de datos.

El Rinconcito de Delphi 10
En el siguiente fragmento de código, vemos el uso de un TIBSQL que se encarga de obtener el número
de factura.
function TDMFacturas.DameNumero(Ejercicio : string) : Integer;
begin
// Ejecuta SP_Dame_Numero_Factura de la base de datos
with SPNumero do
begin
// Cerramos
Close;
// Le pasamos el ejercicio de la factura
Params.ByName('ejercicio').AsString := Ejercicio;
// Ejecutamos la consulta
ExecQuery;
// Recogemos el resultado
Result := FieldByName('numero').AsInteger;
// Liberamos los recursos asociados al IBSQL
FreeHandle;
end;
end;

Buscando en el baúl de los recuerdos


Una de las cosas que primero se intenta hacer, es tratar de implementar búsquedas blandas. Sobre todo
por aquellos que vienen de trabajar con tablas planas tipo Paradox, Dbase, etc. Es decir, usar mecanismos del
tipo FidNearest o Locate. El primero, no está soportado en TIBDataSet y el segundo, sí. Pero las pruebas
que he realizado con él, lo desaconsejan por su lentitud. El mecanismo de búsqueda de Locate, es secuencial.
Así que si trabajamos con tablas con muchas filas, nos puede mandar a tomar un café.
Esta tendencia, a mi juicio, viene dada por tratar de usar Interbase para navegar por todas las filas de la
tabla. A mi juicio, no es esta la mejor filosofia que podemos adoptar con una base de datos de este tipo, ya
que estaremos sobrecargando el servidor de una manera innecesaria.
Puede parecer, y es una vieja creencia, muy extendida desgraciadamente, que darle al usuario la
posibilidad de moverse por sus miles de registros es facilitarle las cosas, pero si tenemos en cuenta el coste
en recursos que esto conlleva, resolveremos que es mas eficiente aplicar el dicho: “Cuantos menos mejor”.
Es decir, sustituir los viejos hábitos y procurar darle al usuario acceso fácil a aquello que realmente necesita.
Con esa filosofía, se ha implementado la rutina de búsqueda que se puede ver a continuación:

UDMFacturas

private
{ Private declarations }
FOriginalSQL : string;

procedure TDMFacturas.DataModuleCreate(Sender: TObject);


begin
Tlocal.StartTransaction;
// Anotamos la sentencia SelectSQL de IBFacturas.
FOriginalSQL := IBFacturas.SelectSQL.Text;
end;

procedure TDMFacturas.Buscar(Campo, Texto : string);


begin
With IBFacturas do
try

El Rinconcito de Delphi 11
// Desconectamos los controles
DisableControls;
// Cerramos
Close ;
// Restablecemos la sentencia original
SelectSQL.Clear;
// FOriginal, contiene la sentencia SelectSQL que se le ponga en diseño
// a IBFacturas
SelectSQL.Add(FOriginalSQL);
// Si no se ha de buscar nada, se abre la tabla y se sale
if Texto = '' then
begin
Open;
Exit;
end;
// Si se ha de buscar, usamos el operador LIKE para hacer busquedas aproximadas
SelectSQL.Add('WHERE ' + Campo + ' LIKE "' + Texto + '%"' );

// Ordenamos por el campo que se hace la busqueda


SelectSQL.Add('ORDER BY '+Campo);
Open ;
finally
// Conectamos los controles
EnableControls;
end;
end;

Evidentemente, se trata de una rutina muy básica y manifiestamente mejorable, pero que puede ilustrar
bien cómo se pueden implementar busquedas flexibles y potentes con un mínimo esfuerzo.
Los que estén acostumbrados a trabajar con Paradox o Dbase, sabrán que para mostrar un conjunto de
datos ordenado, no quedaba más remedio que tener indexada la tabla. Con Interbase, podemos hacer que la
tabla se ordene por el campo que nos apetezca, tenga asociado un indice o no. Evidentemente, si ese campo
tiene asociado un indice, la ordenación se hará mas rápido que si no lo tiene, ya que Interbase lo usará para
ofrecernos los datos ordenados según le pidamos, pero esto, para el desarrollador, es trasparente. Si
queremos ordenar los datos por un campo en concreto, solo tenemos que manipular la propiedad SelectSQL
para añadirle la sentencia adecuada. La rutina que muestra el siguiente fragmento de código se encarga de
ello.
procedure TDMFacturas.Ordena(Campo, Orientacion : String);
var
I : Integer;
begin
with IBFacturas do
try
// Desconectamos los controles
DisableControls;
// Cerramos la tabla
Close;
// ¿ Tenia ya un orden ? Si lo tenia, se lo quitamos
for I := SelectSQL.Count - 1 downto 0 do
if pos('ORDER BY', uppercase(SelectSQL[I])) <> 0 then
SelectSQL.Delete(I);
// Y le añadimos el nuevo
SelectSQL.Add('ORDER BY '+Campo+Orientacion);
// Volvemos a abrir
Open;
finally
// Volvemos a conectar los controles
EnableControls;
end;

El Rinconcito de Delphi 12
end;

Este procedimiento se emplea, por ejemplo, para hacer que la tabla se ordene en respuesta al evento
OnTitleClick de un DBGrid.

UFMFacturas

CBCampo: TComboBox;
private
{ Private declarations }
ColumnaOrdenada : TColumn;
AscDesc : string;

procedure TWFacturas.FormCreate(Sender: TObject);


begin
// En principio, la tabla está ordenada por el código
ColumnaOrdenada := GLista.Columns.Items[0];
// Y ordenada en forma ascendente
AscDesc := '';
// Llenamos CBCampo, con los nombres de los campos
DMFacturas.IBFacturas.Fields.GetFieldNames(CBCampo.Items);
CBCampo.ItemIndex := 0;
end;

procedure TWFacturas.GListaTitleClick(Column: TColumn);


var
I : Integer;
begin
// Se ha vuelto a hacer click en la misma columna
if ColumnaOrdenada = Column then
// Conmutamos el orden
if AscDesc = '' then
AscDesc := ' DESC'
else
AscDesc := ''
else
ColumnaOrdenada := Column;

// Ordenamos la tabla de clientes, por el campo de la columna


DMFacturas.Ordena(Column.FieldName,AscDesc);

// Indicamos que la columna está ordenada


Column.Color := $00E2E2E2;

// El resto de columnas van en clWhite.


for I := 0 to GLista.Columns.Count - 1 do
if not (GLista.Columns.Items[I] = Column) then
GLista.Columns.Items[I].Color := clWhite;
end;

Hasta aquí esta breve panorámica sobre los IBX. Espero que os hayan podido servir de ayuda a los que
os planteais empezar a trabajar con esta colección de componentes. He intentado tanto en este artículo como
en el anterior, que sirvió de introducción, hacer hincapié en los aspectos prácticos más que en los teóricos.
Ojalá haya sabido ser lo suficientemente claro en mis exposiciones. Que lo disfruteis.

El Rinconcito de Delphi 13
Consultas dinámicas o como protegernos de clientes pelmazos.

Una de las cosas que hace que una aplicación sea mas potente e interesante para el usuario, es la
extracción de información de una base de datos. Si importante es el ciclo de mantenimiento de las distintas
tablas que forman un sistema, no menos importante es la explotación de los datos que estas contienen. En mi
opinión debiera ser el primer objetivo a tener en cuenta en el momento de diseñar una base de datos: Que
necesidades de información, presentes, y en la medida de lo posible, futuras, tiene que cubrir nuestro sistema.
La solución clásica es incluir en nuestras aplicaciones toda clase de listados e informes. Para ello se
suele usar la colección de componentes Qreport, proporcionada por el propio Delphi. Estos componentes nos
permiten crear casi cualquier tipo de informe y parece el camino lógico a seguir cuando nos planteamos
diseñar sistemas de explotación de datos. La desventaja que presenta este método es que cada vez que se
necesite modificar o incluir nuevos informes, hay que recompilar nuestro ejecutable.
Mi experiencia me dice que por más informes y listados que incluyamos en nuestros programas, nunca
serán suficientes para los clientes. A esto hay que añadir la primera ley de “los trabajos frustantes”. Cuanto
más elaborado se un informe y mas horas le dediquemos a su planificación y diseño,... menos importancia le
dará nuestro cliente.
Cualquier programador que se precie, ha intentado en algún momento crearse su propio sistema de
listados. Un sistema que le permita incluir listados de todo tipo, a ser posible sin tener que recompilar a cada
momento, y con el menor esfuerzo posible.
Existen en el mercado herramientas que pueden cubrir mas que sobradamente estas necesidades. Delphi
incluyó en sus primeras versiones una herramienta llamada ReportSmith, que mas tarde fué abandonada sin
que nadie llorara lo mas mínimo por ella, todo hay que decirlo. Hay quien habla maravillas de ReportBuilder y
seguro que en algún momento, habreis probado herramientas como FastReport o QRDesing.
Desde luego, la alternativa que voy a explicar, no es la única que se puede seguir. Pero la ventaja que
presenta esta solución, es que es transparente al usuario y... mas barata, que visto como se está poniendo el
patio, no es una razón desdeñable.

Preparando el escenario
Cualquier camino que tomemos, pasa por hacer que nuestra aplicación se comunique con el mundo
exterior. Para ello nos vamos a servir de unos simples ficheros de texto en los que incluiremos las
definiciones de las consultas que queramos y algunas cosas mas.
Para ilustrar lo que aquí vamos a explicar, este artículo se acompaña de un ejemplo en el que el amable
lector, podrá encontrar la implementación completa de cuanto aquí se expone. Partiremos de la pequeña
aplicación que desarrollamos en el número 5 de esta revista para incorporarle el módulo de consultas
Como ya dije, nos vamos a servir de unos sencillos ficheros de texto para almacenar la definición de la
consulta que deseemos ejecutar. Usaremos código SQL en dichas definiciones; código que almacenaremos
en la propiedad SelectSQL de un TIBDataSet y para mostrar los datos, un sencillo DBGrid. Como veis, nada
del otro jueves. En la figura 1, podeis ver la ventana que con todos los elementos que nos harán falta.

El Rinconcito de Delphi 14
Figura 1. La ventana de gestión de consultas

Empezando por el tejado


El módulo usa dos TstringList donde se cargará el contenido del fichero de texto. Para ello nos servimos
del método LoadFromFile, que nos permite especificar el fichero que queremos cargar. Por lo demás, como
se puede ver en el Listado 1, el código es sumamente sencillo y no requiere de más comentarios.

Listado 1. Código para cargar una nueva consulta

private
{ Private declarations }
LstOriginal, LstModificado : TStringList;

procedure TWConsultasSQL.accAbrirExecute(Sender: TObject);


begin
if OD.Execute then
begin
// Cargamos en LstOriginal, el contenido del archivo
LstOriginal.Clear;
LstOriginal.LoadFromFile(OD.FileName);
// Cerramos el dataset
if IBVista.Active then
IBVista.Close;
// Indicamos el fichero que se ha abierto
PNTitulo.Caption := ExtractFileName(OD.FileName);
// Y activamos el botón de ejecutar
accEjecutar.Enabled := LstOriginal.Text <> '';
end;

El Rinconcito de Delphi 15
end;

El código de respuesta del botón Ejecutar, merece que nos detengamos algo más, para explicar la lógica
que se ha seguido.
Normalmente, necesitaremos recuperar un rango de registros de nuestras tablas, lo que implica el uso de
parámetros y la necesidad de facilitar la introducción de los valores para cada uno de esos parámetros por
parte del usuario.
Para esto, podriamos haber usado la propiedad Params del TIBDataSet, pero para esta ocasión me
decidí a usar otra estartegia que, en mi opinión, aporta más posibilidades.
Ya sabreis que Interbase ignora las líneas de código que se encuentran delimitadas por los simbolos “/*
*/”, es decir, que las toma como comentarios. Aprovecharemos este espacio para introducir información
acerca de los parámetros que necesita la consulta para ejecutarse. La información que nos interesa guardar
es: el nombre del parámetro, el tipo de dato y el mensaje para el usuario. Para poder identificar esta
información dentro de los comentarios, la delimitaremos con “{ }” y dentro de estas, cada campo irá
separado por “;”. Para identificar los prámetros, estos los he encerrado entre dos interrogaciones. En el
listado 2, se puede ver un ejemplo de todo lo dicho.

Listado 2. Ejemplo de definición de una consulta y de sus parámetros.

/*
Estas líneas son tratadas como comentarios e ignoradas por Interbase, por lo que son el
lugar ideal para colocar la definición de los parámetros de esta consulta.
{DESDE;D;Desde Fecha;}
{HASTA;D;Hasta Fecha;}
{CLIENTE;N;Codigo del Cliente;}

Cerramos los comentarios


*/
SELECT F.FECHA, C.NOMBRE, SUM(F.BASE_IMP) BASE, SUM(F.TOTAL_IVA) IVA, SUM(F.IMPORTE_TOTAL)
TOTAL
FROM FACTURAS F INNER JOIN CLIENTES C ON(F.CLIENTE=C.CODIGO)
WHERE C.CODIGO=¿CLIENTE? AND (F.FECHA >= ¿DESDE? AND F.FECHA <= ¿HASTA?)
GROUP BY F.FECHA,C.NOMBRE

Una vez definido este protocolo, solo tendremos que procesar la información leida del fichero de texto,
buscando y sustituyendo los parámetros contenidos en la consulta, por los valores que haya introducido el
usuario. Una vez que tengamos este trabajo hecho, obtendremos una nueva sentencia, que asignaremos al
TIBDataSet y lo abriremos. Parte de este código, lo podemos ver en el listado 3

Listado 3. Respuesta al botón Ejecutar.

procedure TWConsultasSQL.accEjecutarExecute(Sender: TObject);


begin
// PideParametros devuelve True si la consulta tiene parámetros y el usuario ha
// introducido la información que se le pide. False, si ha cancelado la operación
if not PideParametros then Exit;
try
Screen.Cursor := crHourGlass;
IBVista.Close;
IBVista.SelectSQL.Clear;
IBVista.SelectSQL.Assign(LstModificado);

El Rinconcito de Delphi 16
try
IBVista.Open;
except
ShowMessage('Error de sintaxis: '+#13+IBVista.SelectSQL.Text);
raise;
end;
finally
Screen.Cursor := crDefault;
end;
end;

Las interioridades del invento


Ya tenemos todo lo necesario para hacer funcionar nuestro sistema de consultas dinámicas. Solo nos
queda dar el toque final y hacer atractivo al usuario la introducción de los valores que definirán el rango de
registros que nos devolverá la consulta.
Para esta tarea, vamos a construir una ventana donde se pedirá al usuario que introduzca los valores. El
aspecto de esta ventana, lo podemos ver en la figura 2

Figura 2. Ventana de petición de valores


Para ayudarnos en la tarea de edición y gestión de parámetros, he creado una clase que se encarga de
asociar a cada parámetro un control de edición. He preferido usar el control TmaskEdit, porque se pueden
definir máscaras de edición, lo que aprovecharemos para, por ejemplo, solicitar fechas.
En el listado 4, podemos ver la definición de la clase.

Listado 4. Definición de la clase Tparametros

TParametros = class(TObject)
protected
FCadenaGen : string; // Cadena que contiene la definición del parámetro
FNombre : string; // Nombre del parámetro
FTipo : string; // Tipo del parámetro
FRotulo : string; // Rótulo que se mostrará al editar el parámetro
FEditor : string; // Valor que ha introducido el usuario
FEtiqueta : TLabel; // Label que mostrará el mensaje

El Rinconcito de Delphi 17
FControl : TMaskEdit; // Control donde se editará el parámetro
FPropietario : TComponent; // Ventana donde se mostrará el control
procedure SetEditor(Valor : string);
public
property Nombre : string read FNombre write FNombre;
property Tipo : string read FTipo write FTipo;
property Rotulo : string read FRotulo write FRotulo;
property Editor : string read FEditor write SetEditor;
property Etiqueta : TLabel read FEtiqueta write FEtiqueta;
property Control : TMaskEdit read FControl write FControl;
property Propietario : TComponent read FPropietario write FPropietario;
constructor Create;
destructor Destroy; override;
procedure CargaDatos(Propietario : TComponent; ACadena : string);
end;

El método CargaDatos recibe dos argumentos: El contenedor donde se mostrarán los MaskEdits y la
cadena que contiene el parámetro que queremos gestionar. Es decir, la cadena delimitado por los signos “{}”.
La ventana de petición de datos, cuenta con un método que recibe como argumento un TstringList con
todos los parámetros. Por cada uno de ellos, crearemos un objeto del tipo Tparametros y lo almacenaremos
en un TstringList, valiendonos del método AddObjects. Parte de este código, se puede ver en el listado 5

Listado 5.

var
P : TParametros;
I,L,T : Integer;
begin
// Cargamos los parámetros
FParametros := TStringList.Create;
for I := 0 to AParametros.Count - 1 do
begin
try
P := TParametros.Create;
P.CargaDatos(Contenedor,AParametros.Strings[I]);
// Usamos el método AddObject para cargar en cada elemento de la lista
// un objeto del tipo Tparametros.
Fparametros.AddObject(P.Nombre,TParametros.Create);
Tparametros(FParametros.Objects[I]).CargaDatos(Contenedor,
Aparametros.Strings[I]);
finally
P.Free;
end;
end;
end;

Una vez que el usuario ha introducido la información que se le solicita, solo nos queda volver a procesar
la cadena que leimos del fichero, y sustituir los parámetros por los valores que haya introducido el usuario,
asignar a la propiedad SelectSQL del TIBDataSet esa cadena ya procesada y abrirlo.

El Rinconcito de Delphi 18
Para terminar
Como siempre, el código del ejemplo que acompaña a este artículo, podrá iluminar mas que mis torpes
explicaciones todo lo dicho.
Como dije al principio del artículo, este trabajo es fruto de la necesidad y no pretende ser, de hecho no lo
es, una maravilla tecnológica. Seguro que se podria escribir de 200 formas mas eficientes, pero he querido
compartirlo con vosotros por si a alguien le pudiese servir como punto de partida. O por si alguno hace un
estudio sobre rarezas, ya sabe que ejemplo incluir :-).

El Rinconcito de Delphi 19
Viajando a las entrañas de nuestra base de datos

Vamos a ver como podemos extraer información de la estructura de nuestra base de datos, usando
el componente IBExtract de la paleta Interbase.

Siguiendo con la serie sobre los componentes IBX de la paleta Interbase, vamos a ver en esta ocasión el
componente TIBExtract. Este componente sirve para extraer información de la estructura de una base de
datos. No es un componente de uso frecuente en una aplicación, pero nos puede ser muy útil para obtener la
estructura de la base de datos. Y si el lector es del tipo emprendedor, quizás se anime a crearse su propio
programa para manipular bases de datos de Interbase. ¡Quien sabe!. A lo mejor conseguimos entre todos
mejorar ese engendro infumable llamado IBConsole.
Para mostrar la funcionalidad de este componente, he desarrollado un pequeño ejemplo que se encarga
de mostrar y extraer toda la información de una base de datos y guardarla en un fichero de texto. Este
fichero nos puede servir para crear la base de datos desde cero.
Pero ¿donde se guarda la información que nos interesa?. Seguro que la mayoría sabe que Interbase
cuenta con una serie de tablas de sistema, donde se guarda toda la información referente a las tablas,
dominios, triggers , relaciones y demás elementos que hemos ido definiendo cuando creamos la base de datos.
El nombre de estas tablas comienza por el prefijo RDB$ seguido por el nombre mas o menos descriptivo
acerca de lo que contienen. Así, por ejemplo, el nombre de las tablas y vistas, se obtiene de listar la tabla
“RDB$RELATIONS”.En “RDB$PROCEDURES”, podremos encontrar el listado de procedimientos, etc.
Es de estas tablas donde el IBExtract bucea para ofrecernos la información que le solicitemos. Y la
solicitud no puede ser mas simple. Una sola llamada a un método del objeto, con los parámetros apropiados,
basta para obtener la estructura de cualquiera de los elementos de nuestra base de datos.
Para obtener el listado de los distintos elementos, he usado unas constantes con la definición de la
consulta que será necesaria para obtener de las tablas de sistema, el listado de tablas, dominios, generadores,
procedimientos, etc. El código de dichas consultas se puede ver en el listado 1

Listado 1. Definición de constantes.


const
// Listado de Dominios
DomainSQL = 'SELECT RDB$FIELD_NAME FROM RDB$FIELDS
WHERE RDB$FIELD_NAME NOT LIKE "RDB$%"';
// Listado de Tablas
TablasSQL = 'SELECT RDB$RELATION_NAME FROM RDB$RELATIONS
WHERE (RDB$SYSTEM_FLAG <> 1 or RDB$SYSTEM_FLAG IS NULL) AND
(RDB$VIEW_SOURCE IS NULL)';
// Listado de vistas
VistasSQL = 'SELECT RDB$RELATION_NAME FROM RDB$RELATIONS
WHERE (RDB$SYSTEM_FLAG <> 1 or RDB$SYSTEM_FLAG IS NULL) AND
(RDB$VIEW_SOURCE IS NOT NULL)';
// Listado de Procedimientos almacenados
ProcedSQL = 'SELECT RDB$PROCEDURE_NAME FROM RDB$PROCEDURES
WHERE (RDB$SYSTEM_FLAG <> 1 or RDB$SYSTEM_FLAG IS NULL)';
// Listado de Funciones
FuncioSQL = 'SELECT RDB$FUNCTION_NAME FROM RDB$FUNCTIONS
WHERE (RDB$SYSTEM_FLAG <> 1 or RDB$SYSTEM_FLAG IS NULL)';
// Listado de Generadores
GeneraSQL = 'SELECT RDB$GENERATOR_NAME FROM RDB$GENERATORS
WHERE (RDB$SYSTEM_FLAG <> 1 or RDB$SYSTEM_FLAG IS NULL)';
// Listado de Excepciones
ExcepcSQL = 'SELECT RDB$EXCEPTION_NAME FROM RDB$EXCEPTIONS';

El Rinconcito de Delphi 20
// Listado de Triggers
TriggeSQL = 'SELECT RDB$TRIGGER_NAME FROM RDB$TRIGGERS
WHERE (RDB$SYSTEM_FLAG <> 1 or RDB$SYSTEM_FLAG IS NULL)';

He dispuesto también una serie de TcheckListBox, donde se cargará la información obtenida de


ejecutar dichas sentencias. Para esto me he servido de un TIBDataSet al que asigno a la propiedad
SelectSQL la definición correspondiente. De todo esto se encarga el procedimiento CargaListas. La
definición de de este procedimiento se puede ver en el listado 2. Este procedimiento es llamado en el evento
OnCreate de la ventana, con lo que cuando entremos en ella, tendremos disponible la información. También
he colocado un Tmemo, donde se podrá visualizar la información que vayamos extrayendo de la base de
datos.
Esta ventana cuenta con varios botones que nos permitirán extraer la información de toda la base de
datos, de los objetos que seleccionemos en los TcheckListBox y guardarla en un fichero de texto.

Listado 2. Definición del procedimiento CargaListas

procedure TWExtraer.CargaListas(Lista : TCheckListBox; const Sentencia : String);


begin
// IBMeta es un TIBDataSet
with IBMeta do
begin
// Lo cerramos para poder manipular el SelectSQL
Close;
// Limpiamos el contenido de SelectSQL
SelectSQL.Clear;
// Añadimos la sentencia que nos pasan
SelectSQL.Add(Sentencia);
// Abrimos y nos vamos a la primera fila
Open;
First;
// Limpiamos el contenido de la lista y recorremos todas las filas, añadiendo
// la información encontrada
Lista.Items.Clear;
while not EOF do
begin
Lista.Items.Add(Fields[0].AsString);
Next;
end;
end;
end;

Ahora que tenemos el escenario dispuesto, es hora de pasar a mayores y ver como extraer la
información deseada.
Como ya dije al principio, el método es muy sencillo. Basta con llamar al procedimiento ExtractObject
cuyo prototipo es el siguiente:
ExtractObject(ObjectType:TExtractObjectTypes;
ObjectName:String='';
ExtractTypes:TExtractTypes=[]);

Como se puede ver, el procedimiento acepta tres argumentos, de los que los dos últimos son opcionales.
Los parámetros son: ObjectsType. Indica el tipo de objeto vamos a extraer y es del tipo
TextractObjectTypes. La definición de este tipo es:
TExtractObjectTypes =

El Rinconcito de Delphi 21
(eoDatabase, eoDomain, eoTable, eoView, eoProcedure, eoFunction,
eoGenerator, eoException, eoBLOBFilter, eoRole, eoTrigger, eoForeign,
eoIndexes, eoChecks, eoData);

Cabe señalar que si indicamos el objeto eoDatabase en este argumento, se extraerá el metadata de toda
la base de datos.
El segundo argumento es ObjectName de tipo string, donde podremos indicar el nombre del objeto que
queremos extraer. Si no indicamos nada, en este argumento, se extraerán todos los objetos del tipo señalado
en el primer argumento.
El tercer argumento es ExtractTypes y es del tipo TextractTypes. Indica el tipo de extracción que se
va a hacer del objeto. Su definición es:
TExtractType =
(etDomain, etTable, etRole, etTrigger, etForeign,
etIndex, etData, etGrant, etCheck, etAlterProc);

TExtractTypes = Set of TExtractType;

En el ejemplo que acompaña a este artículo, podrá ver los resultados que ofrece la llamada a este método
con la tabla de una base de datos.
El resultado de la extracción se almacena en la propiedad Items del TIBExtract.

Poco mas necesitamos para poder usar este componente. Solo nos resta indicar el TDataBase y el
TTransaction al que estará conectado el componente y hacer las llamadas a ExtractObject que nos interesen.
Sencillo, rápido y eficaz. ¡El sueño de cualquier programador!. ¿No creéis?.

El Rinconcito de Delphi 22
IBX. El toque final
A lo largo de esta serie de artículos, hemos visto los distintos aspectos acerca del trabajo con
estos componentes para el acceso a bases de datos Interbase. Completaremos en este artículo la
visión con el repaso a los componentes que se agrupan en la paleta “Interbase Admin.

El repaso a IBX no estaría completo si no nos detuviésemos en esos componentes que se agrupan en la
paleta “Interbase Admin.”. Así mismo, también veremos las novedades que han aparecido con las distintas
versiones que el autor ha ido liberando desde que se inicio esta serie de artículos. Para la realización del
ejemplo que acompaña a este artículo, se ha usado la versión 5.3 de los IBX. Para los que cuenten con la
versión 6 de Delphi, está disponible la versión 6.3. Así mismo, se ha de contar con la versión 6.x de Interbase,
para que los componentes que vamos a ver, se instalen en Delphi.

IBBackupService. O como hacer copias de seguridad.


Sin duda, una de las operaciones de mantenimiento mas importantes en una base de datos, es la copia de
seguridad. No hace falta convencer a nadie, a estas alturas, de la importancia de esta operación. Bueno, “a
nadie” no es el término adecuado. Conozco a gente que piensa que su disco duro va a ser eterno o que los
accidentes nunca le van a pasar a él. Estos, son de los que se enteran demasiado tarde de que los usuarios
informáticos se dividen en dos: Los que han tenido problemas con el disco duro y los que los van a tener.

Estos usuarios siempre tienen las mismas excusas: “Los programas de copia de seguridad son muy
complejos”. “Se pierde demasiado tiempo haciendo la copia”. O una de mis preferidas: “¡Pues vaya un
programa que se rompe si se fastidia el disco!”. Quien no haya oído nunca uno de estos argumentos, tiene mi
permiso para saltarse esta parte del artículo. Para los que les suene esta música, ¡presten atención!.
Realmente, hacer una copia de seguridad con este componente, es una tarea de lo mas sencilla. Basta
con indicarle la base de datos origen y el archivo GBK destino, llamar al método ServiceStart y configurar
las opciones que deseemos y ya está. Vean si no el listado 1, donde se puede observar la respuesta a la
pulsación de un botón que inicia el proceso de Copia de una base de datos.

Listado 1. Copia de seguridad

// Realizamos la Copia de seguridad


procedure TWUtilidades.TBCopiaClick(Sender: TObject);
begin
with DMServicios.IBBackup do
begin
Active := True;
try
Screen.Cursor := crHourGlass;
BackupFile.Clear;
MCopia.Lines.Clear;
// Cargamos la opciones que nos indique el usuario
Options := [];
if chkCheckSum.Checked then Options := Options + [IgnoreCheckSums];
if chkIgnoreLimbo.Checked then Options := Options + [IgnoreLimbo];
if chkStructure.Checked then Options := Options + [MetadataOnly];
if chkGarbage.Checked then Options := Options + [NoGarbageCollection];
if chkTransportable.Checked then Options := Options + [NonTransportable];

El Rinconcito de Dephi 23
// Cargamos la información que nos han indicado
// Base de datos origen
DatabaseName := EOrigen.Text;
// Fichero GBK destino
BackupFile.Add(EDestino.Text);
// ¿Ver el progreso de la operación?
Verbose := chkVerbose.Checked;
// Comenzar
ServiceStart;
// Si quieren ver el progreso
if Verbose then
begin
// Mientras no se llegue al final...
While not Eof do
// Cargamos las lineas de salida en el memo
MCopia.Lines.Add(GetNextLine);
// Avisamos que hemos terminado
MCopia.Lines.Add('¡ PROCESO TERMINADO !');
end;
finally
Active := False;
Screen.Cursor := crDefault;
end;
end;
end;

Interbase permite definir una serie de opciones para hacer la copia de seguridad. Estas opciones
podemos definirlas en el componente mediante la propiedad Options. Las opciones posibles son:
IgnoreCheckSums : Si se fija a True, desactiva la comprobación de la integridad de los datos en el
análisis página a página que se hace al efectuar el Backup.
IgnoreLimbo : En determinadas circunstancias, puede que queden transacciones perdidas, es decir, que
no se han cerrado correctamente. Si fijamos a True esta propiedad, estas transacciones serán ignoradas.

MetadataOnly: Incluye en la copia de seguridad, solo la estructura de la base de datos. Esta opción es
perfecta para cuando nos interesa “vaciar” una base de datos. Por ejemplo, una vez terminado el desarrollo
de nuestra aplicación, para borrar los datos de pruebas que hayamos introducido.
NoGarbageCollection: Durante la copia de seguridad, se marcan las páginas que contienen versiones
antiguas de los datos, como candidatas a ser eliminadas. Si marcamos esta opción a True, no se marcaran y
no serán eliminadas.
OldMetadataDesc: Nos permite mantener el metadata de nuestra base de datos en el formato antiguo

NonTransportable: El valor por defecto, nos permite obtener una copia que puede ser restaurada en
otra máquina con otro sistema operativo. Si se fija a True, desactivamos esta posibilidad.
Si nos fijamos en la propiedad BackupFile es un Tstrings, cuando lo lógico seria pensar en una string
donde almacenar el nombre (con la ruta) del archivo destino. Esto es así, porque tenemos la posibilidad de
repartir nuestra copia de seguridad en varios archivos.

El Rinconcito de Dephi 24
IBRestoreServices. Restaurar copias.
Obviamente, no tendría sentido tener la posibilidad de sacar copias de seguridad, si luego no contásemos
con un sistema capaz de poder restaurarlas. Para ello contamos con este servicio.
La forma de uso es tan sencilla como la del IBBackupService. Unas pocas líneas de código bastan para
dotar a nuestras aplicaciones de la posibilidad de restaurar copias de nuestra base de datos. Ved si no el
listado 2, donde se ofrece un ejemplo del uso de este componente.

Listado 2. Restaurar una copia de seguridad

// Restaurar desde una copia de seguridad


procedure TWUtilidades.TBRestoreClick(Sender: TObject);
begin
with DMServicios.IBRestore do
begin
Active := True;
try
Screen.Cursor := crHourGlass;
DatabaseName.Clear;
BackupFile.Clear;
MRestore.Lines.Clear;

// Cargamos las opciones que nos indiquen


Options := [];
if chkIndices.Checked then Options := Options + [DeactivateIndexes];
if chkShadow.Checked then Options := Options + [NoShadow];
if chkValidate.Checked then Options := Options + [NoValidityCheck];
if chkReplace.Checked then Options := Options + [Replace];
if chkCreate.Checked then Options := Options + [CreateNewDB];
// ¿Quieren ver el progreso?
Verbose := chkVerboseRestore.Checked;

PageBuffers := 3000;
PageSize := 4096;
// Indicamos la base de datos destino
DatabaseName.Add(EDb.Text);
// El archivo GBK origen
BackupFile.Add(EGbk.Text);
// Cerramos la conexión con la base de datos.
// En una aplicación real, tendriamos que cerrar todas las ventanas activas
// que tengan una conexión con tablas de la base de datos
DMMain.Desconectar;
// Comenzar
ServiceStart;
// Si se quiere ver el progreso...
if Verbose then
begin
While not Eof do
MRestore.Lines.Add(GetNextLine);
MRestore.Lines.Add('¡ PROCESO TERMINADO !');
end;
finally
Active := False;
// Vovemos a conectar con la base de datos
DMMain.Conectar;
Screen.Cursor := crDefault;
end;
end;

El Rinconcito de Dephi 25
end;

Las propiedades son similares a las ya vistas para el Backup. Lógicamente, las opciones que tenemos
disponibles son distintas en esta ocasión.
DeactivateIndexes:: Si fijamos esta opción a True, los indices no serán reconstruidos durante el
proceso de restauración.
NoValidityCheck: Si establecemos a True esta opción, desactivaremos la verificación de la integridad
de los datos.

Replace: La base de datos existente, será sobreescrita. Esta opción es para los amigos de las
emociones fuertes.

IBScript. O como rejuvenecer nuestra base de datos.


Es normal que una vez terminado el desarrollo de nuestra aplicación, se necesiten hacer ajustes en la
estructura de la base de datos, bien para añadirle tablas, modificar Triggers, crear indices... Este componente
nos permite llevar a cabo esta tarea, sin demasiado esfuerzo.
El componente cuenta con la propiedad Script. Un Tstring al que le pasaremos (desde un archivo de
texto, por ejemplo) el código que queremos ejecutar.
Disponemos de un método llamado Validate que nos permite revisar la sintaxis del código que queremos
ejecutar, de modo que podremos asegurarnos de que al menos por esa razón el Script no fallará.
Pero no solo por esa razón puede fallar. Imaginemos que intentamos crear un procedimiento almacenado
que ya existe previamente en la base de datos. La llamada a Validate, no detectará ningún error, sin
embargo, la actualización fallará pues no pueden haber dos procedimientos con el mismo nombre. ¿Que
haremos en ese caso?. El componente nos provee de dos eventos para poder controlar esas situaciones.
OnExecuteError y OnParseError. En el primer evento, además, disponemos del argumento Ignore . Si
cuando se produce un error le damos como valor True, el error será ignorado y la ejecución del Script
continuará.

IBFilterDialog. ¡Que bonito es no tener que trabajar!


Algunas veces se agradece que a uno le den el trabajo hecho. Se siente uno tan bien, que hasta se
predispone a no poner demasiadas pegas a los regalos. Hay un refrán muy extendido (al menos en España)
que se podría aplicar perfectamente en esta ocasión. “A caballo regalado, no le mires el dentado”. Pues eso
haremos en esta ocasión. No mirarle demasiado el dentado a este componente para búsquedas en una tabla
de nuestra base de datos. La interfaz, no es que sea una maravilla de imaginación y buen gusto. La operativa
podría ser algo mas amigable, pero a ver quien es el guapo que con menos, consigue más. Y es que
realmente, hay poco que hablar acerca del funcionamiento de este componente. Basta indicarle el IBDataSet
al que se enlazará y escribir una sola línea de código, invocando al método Execute, para obtener un cuadro
de diálogo que nos permite buscar por cualquier campo de nuestra tabla. Con búsquedas sensibles o no a las
mayúsculas y minúsculas, búsquedas parciales, etc. Tambien nos permite definir “alias” para nuestros
campos, de forma que al usuario se le muestren nombres mas intuitivos que los que solemos poner los
programadores cuando diseñamos una base de datos.

El Rinconcito de Dephi 26
Con los ejemplos que acompañan a este artículo, he incluido una versión del componente con la interfaz
traducida al “cristiano”, dado que de origen vienen en un perfecto inglés (faltaría mas).

Despedida y cierre.
A lo largo de esta serie de artículos he pretendido dar una visión amplia de estos componentes. He hecho
especial hincapié en el IBDataset, ya que me parece que es, con mucho, el que más se usa en el desarrollo
de una aplicación. Espero que mi proverbial torpeza cuando de explicar las cosas se trata, no haya sido un
obstáculo para los que se acercan a estos componentes. Y sobre todo, espero que estos artículos hayan sido
útiles. Solo con esa intención se escribieron.

El Rinconcito de Dephi 27

También podría gustarte