Inter Base

download Inter Base

of 28

Transcript of Inter Base

InterbaseJos [email protected]

IBX

Cedida su publicacin a el Rinconcito de Delphi

No est autorizada su reproduccin sin permiso expreso de su autor

Autor: Jos Alonso - [email protected]

IBX. Conexin directa a InterbaseCon los componentes IBX, podemos tener una conexin 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 disponamos 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 mquinas donde queramos hacer correr nuestra aplicacin. A cambio, nos ofrecen la posibilidad de soluciones genricas. Con la versin 5 de Delphi, una nueva pestaa nos ofrece la posibilidad de una relacin mas estrecha entre nuestra aplicacin y los datos... siempre que nuestro soporte de datos sea Interbase. En la pestaa 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 relacin que mantendr nuestra aplicacin con Interbase, ser la de estar continuamente haciendo peticiones de datos al servidor y escuchando las respuestas a estas peticiones. Cmo solicitamos datos a Interbase?. Mediante el uso de los componentes (en este caso los IBX). En este artculo no tratar los componentes TIBTable,TIBQuery,TIBStoredProc ni TIBUpdateSQL, por considerar que estn solamente para ofrecer compatibilidad y permitir una migracin menos traumtica a las aplicaciones basadas en el BDE. Aunque si me permits una opinin, 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 conexin 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 mquina o a bases de datos situadas en mquinas 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 mquina remota, la forma de conectarse a ella, variara dependiendo del protocolo de comunicacin que utilicemos. Por ejemplo, si usamos el protocolo TCP/IP la forma tpica de conectarnos sera: IBDatabase1.DatabaseName := Morgan:C:\Contabilidad\Datos\Gestion.gdb

El Rinconcito de Delphi

1

Pero TIBDatabase, no solo se ocupa de la conexin con la base de datos. Tambin encontraremos propiedades y mtodos que nos ofrecen informacin de la conexin y de la propia base de datos: Si queremos saber si la conexin con el servidor se ha establecido, podemos usar la funcin TestConnected que nos devolver True si la conexin se ha efectuado y False en caso contrario. IBDatabase1.Connected := True; If IBDatabase1.TestConnected then ShowMessage(La conexin se ha establecido con xito) Else ShowMessage(Fallo al intentar conectar con + IBDatabase1.DatabaseName); Podemos especificar el tiempo que queremos que dure la conexin con la base de datos mediante la propiedad IdleTimer, la cual admite un valor entero que expresa la cantidad de milisegundos que durar la conexin o cero para que la conexin sea permanente. Antes de establecer la conexin con la base de datos, tenemos que definir los parmetros 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 diseo de una forma cmoda, con el editor de propiedades al cual se puede acceder mediante el botn derecho del ratn y escogiendo la opcin Database Editor. La figura 1, muestra el aspecto de este editor.

Figura 1. La ventana de configuracin 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 adems 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 transaccin 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 definicin y manipulacin de datos, trabajan en el contexto de una o mas transacciones. Una transaccin 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 explcito 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 aplicacin es obligatorio que haya al menos una transaccin, aunque lo normal es que usemos varias, dependiendo de las necesidades y requerimientos del caso. Es posible asignar una transaccin a un componente TIBDatabase de manera que este sea esta la transaccin por defecto que se usar, de no indicar otra cosa, al asociar cualquiera de los componentes de manipulacin de datos al TIBDatabase. Para ello, el TIBDatabase cuenta con una propiedad: DefaultTransaction donde podremos indicar cual ser la transaccin 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 operacin a la que habremos de prestar ms atencin a la hora de configurar el componente TIBTransaction ser la propiedad Params. Para facilitarnos la tarea de configurar correctamente la transaccin, podemos contar con el editor que vemos en la figura 2 y que como siempre, se obtiene a travs de pulsar el botn derecho del ratn sobre el componente.

Figura 2. La ventana de configuracin de TIBTransaction.

Escapa al objetivo de este artculo 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 mayora 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 mayora de las ocasiones.

El Rinconcito de Delphi

3

Tpicamente, 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 mtodos: StartTransaction da comienzo a la transaccin. Una vez llamado este mtodo, todas las operaciones que se realicen en la base de datos, habrn de confirmarse o cancelarse invocando a los siguientes mtodos. Commit termina la transaccin confirmando los cambios que se hayan producido. La transaccin se cierra y se liberan los recursos. Rollback termina la transaccin descartando los cambios que se hayan producido. La transaccin se cierra y se liberan los recursos. Como habrn observado, tanto Commit como Rollback hacen que las conexiones con las tablas se cierren, con lo que una vez invocados estos mtodos habra que volver a abrir la transaccin y restablecer las conexiones con las tablas para que pudisemos ver los datos. Si deseamos confirmar o cancelar los cambios, sin que la conexin con la transaccin se cierre, podemos usar los mtodos CommitRetaining o RollbackRetaining que hacen lo mismo que los mtodos anteriores, pero sin perderse la conexin con los datos. El uso tpico de estos mtodos se asemejara a lo siguiente: Procedure TData.DataCreate( Sender: Tobject); Begin // Al crearse el Datamodule, iniciamos la transaccin IbTransaction1.StartTransaction; End; Procedure Tdata.DataDestroy(Sender: Tobject); Begin // Al cerrar el Datamodule, cerramos la transaccin... 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;

TIBDataSetEste componente ser el que centre gran parte de nuestra atencin, ya que ser el que usemos preferentemente en nuestra aplicacin. Con l, podremos acceder a una tabla o vista de nuestra base de datos y realizar las operaciones tpicas de mantenimiento de la misma. Como descendiente de la clase TDataSet, mantiene con esta propiedades y mtodos 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 asignacin 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 automticamente en la propie dad Transaction del TIBDataSet. Una de las quejas ms 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 operacin de apertura de una tabla, genera un buen puado 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 instruccin SQL vlida para insertar una o varias filas en la tabla. DeleteSQL. Almacena la instruccin SQL vlida para borrar una o varias filas de la tabla. ModifySQL. Almacena la instruccin SQL vlida para modificar una o varias filas de la tabla. RefreshSQL. Almacena la instruccin SQL vlida para releer la informacin de una o varias filas. SelectSQL. Almacena la instruccin SQL vlida para obtener un conjunto de datos de la tabla. El componente dispone de unos asistentes que nos facilitan la introduccin de estas

El Rinconcito de Delphi

5

sentencias de una forma visual. Para acceder a ellos solo hay que hacer clic con el botn derecho del ratn y escoger la opcin 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 mtodos y sus eventos, requieren que nos demos un respiro, tomemos aire y lo abordemos con la suficiente profundidad en un prximo artculo. Pero para abrir boca, sugiero al amable lector que mire el ejemplo que acompaa a este artculo y compruebe la potencia que nos proporciona este componente. Se trata de una pequea aplicacin cuyo objetivo es rescatar filas de una tabla. Las condiciones que deban cumplir las filas son totalmente configurables. Se trata de un ejemplo de cmo manejar la propiedad SelectSQL de un TIBDataSet en tiempo de ejecucin, amn del uso de ciertos mtodos de TIBDatabase.

El Rinconcito de Delphi

7

Trabajando con TIBDaSet.Se ver como se puede configurar el componente, paso de parmetros, 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 ocasin que en una futura entrega, se veria mas extensamente este ultimo componente, que sin duda centrar el 99% del trabajo con datos en nuestra aplicacin. Como lo prometido es deuda, aqu va esta segunda entrega.

TIBDataSet. El centro de operacionesDefinicin de los datosPara una mejor comprensin y en el convencimiento de que un ejemplo vale ms que mil explicaciones, dedicar el artculo a desarrollar una pequea aplicacin de entrada de artculos y facturas (que original!) Una advertencia. Si quereis probar el ejemplo que acompaa al artculo y teneis la versin 6 de Interbase, habreis de recrear la base de datos ejecutando el script que se acompaa, ya que la base de datos est creada con Interbase 5.6. Tambien ser bueno sealar que la versin de los componentes con la que se ha desarrollado el ejemplo, es la versin 4,52. Podeis averiguar la versin que teneis instalada, accediendo al men contextual de cualquiera de los componentes de la paleta Interbase. Si los vuestros no estn actualizados, podeis descargar la actualizacin de la pgina de Borland. Bien. Metmonos 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. /*Definicin 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; /*Definicin de generadores*/ CREATE GENERATOR CLIENTES; CREATE GENERATOR FACTURAS; /*Definicin 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 nmero de factura. No hay ejercicio activo"; CREATE EXCEPTION FAC_NO_NUMERO "No se ha indicado el numero de factura";

El Rinconcito de Delphi

8

/*Definicin 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 artculo: El trabajo con los IBX.

El mdulo de datos principalCreamos un mdulo de datos donde situaremos un TIBDatabase y un TIBTransaction. Este mdulo de datos se crea con la aplicacin y se destruye cuando esta finaliza. Configuramos debidamente ambos componentes para que podamos establecer la conexin con la base de datos. Para ello, basta con que sealemos en la propiedad DatabaseName del IBDatabase la ruta completa donde se encuentra nuestra base de datos e indicar en DefaultTransaction la transaccin por defecto. El Rinconcito de Delphi 9

Si se observa el cdigo, aprovechamos el evento OnCreate del DataModule, para establecer la conexin con la base de datos e iniciar la transaccin.procedure TDMMain.DataModuleCreate(Sender: TObject); begin try // Conectamos! DB.Connected := True; // TestConnected, devuelve True si se ha podido conectar. // Aprovechamos para iniciar la transaccin if DB.TestConnected then TPermanente.StartTransaction; except ShowMessage('No se ha podido establecer la conexin con la base de datos'); // veamos el error que nos d raise // Y terminamos Application.Terminate; end; end; // Al destruise el mdulo de datos, cerramos la transaccin procedure TDMMain.DataModuleDestroy(Sender: TObject); begin TPermanente.Commit; end;

Poco mas necesitaremos en este mdulo. Solo indicar que la transaccin la he configurado con un nivel de aislamiento READ COMMITED, ya que este nivel posibilita que la transaccin vea todos los datos confirmados en la base de datos y actualice filas confirmadas por otras transacciones simultneas. Los dems mdulos 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 cdigo es mas que suficiente para entender lo que hace.

Facturas, facturas y ms facturas.No, no preocuparos, que no os voy a aburrir con mi estado financiero. Este artculo podra convertirse en un valle de lgrimas!. Mi intencin es hablaros del mdulo de datos que gestiona todo lo realcionado con los datos de las facturas y sus lneas de detalle. Tenemos dos TIBDataSet (IBFacturas e IBDetalles) unidos con una relacin 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 . Adems, hay que aadir a la sentencia SelectSQL de IBDetalles, la clausula WHERE FACTURA=:CODIGO. El parmetro 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 parmetro, 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 visualizacin 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 artculos. En este mdulo de datos, tambien se han incluido cuatro TIBSql que nos servirn para acceder a los Stored Procedures de la base de datos.

El Rinconcito de Delphi

10

En el siguiente fragmento de cdigo, vemos el uso de un TIBSQL que se encarga de obtener el nmero 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 bal de los recuerdosUna de las cosas que primero se intenta hacer, es tratar de implementar bsquedas 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 bsqueda 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 hbitos y procurar darle al usuario acceso fcil a aquello que realmente necesita. Con esa filosofa, se ha implementado la rutina de bsqueda que se puede ver a continuacin: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 diseo // 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 bsica y manifiestamente mejorable, pero que puede ilustrar bien cmo se pueden implementar busquedas flexibles y potentes con un mnimo esfuerzo. Los que estn acostumbrados a trabajar con Paradox o Dbase, sabrn que para mostrar un conjunto de datos ordenado, no quedaba ms 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 ordenacin se har mas rpido que si no lo tiene, ya que Interbase lo usar para ofrecernos los datos ordenados segn 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 aadirle la sentencia adecuada. La rutina que muestra el siguiente fragmento de cdigo 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 aadimos 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 cdigo 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 panormica sobre los IBX. Espero que os hayan podido servir de ayuda a los que os planteais empezar a trabajar con esta coleccin de componentes. He intentado tanto en este artculo como en el anterior, que sirvi de introduccin, hacer hincapi en los aspectos prcticos ms que en los tericos. Ojal haya sabido ser lo suficientemente claro en mis exposiciones. Que lo disfruteis.

El Rinconcito de Delphi

13

Consultas dinmicas o como protegernos de clientes pelmazos. Una de las cosas que hace que una aplicacin sea mas potente e interesante para el usuario, es la extraccin de informacin 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 explotacin de los datos que estas contienen. En mi opinin debiera ser el primer objetivo a tener en cuenta en el momento de disear una base de datos: Que necesidades de informacin, presentes, y en la medida de lo posible, futuras, tiene que cubrir nuestro sistema. La solucin clsica es incluir en nuestras aplicaciones toda clase de listados e informes. Para ello se suele usar la coleccin de componentes Qreport, proporcionada por el propio Delphi. Estos componentes nos permiten crear casi cualquier tipo de informe y parece el camino lgico a seguir cuando nos planteamos disear sistemas de explotacin de datos. La desventaja que presenta este mtodo es que cada vez que se necesite modificar o incluir nuevos informes, hay que recompilar nuestro ejecutable. Mi experiencia me dice que por ms informes y listados que incluyamos en nuestros programas, nunca sern suficientes para los clientes. A esto hay que aadir la primera ley de los trabajos frustantes. Cuanto ms elaborado se un informe y mas horas le dediquemos a su planificacin y diseo,... menos importancia le dar nuestro cliente. Cualquier programador que se precie, ha intentado en algn 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 mnimo por ella, todo hay que decirlo. Hay quien habla maravillas de ReportBuilder y seguro que en algn 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 solucin, es que es transparente al usuario y... mas barata, que visto como se est poniendo el patio, no es una razn desdeable.

Preparando el escenarioCualquier camino que tomemos, pasa por hacer que nuestra aplicacin 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 artculo se acompaa de un ejemplo en el que el amable lector, podr encontrar la implementacin completa de cuanto aqu se expone. Partiremos de la pequea aplicacin que desarrollamos en el nmero 5 de esta revista para incorporarle el mdulo de consultas Como ya dije, nos vamos a servir de unos sencillos ficheros de texto para almacenar la definicin de la consulta que deseemos ejecutar. Usaremos cdigo SQL en dichas definiciones; cdigo 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 harn falta.

El Rinconcito de Delphi

14

Figura 1. La ventana de gestin de consultas

Empezando por el tejadoEl mdulo usa dos TstringList donde se cargar el contenido del fichero de texto. Para ello nos servimos del mtodo LoadFromFile, que nos permite especificar el fichero que queremos cargar. Por lo dems, como se puede ver en el Listado 1, el cdigo es sumamente sencillo y no requiere de ms comentarios.

Listado 1. Cdigo 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 botn de ejecutar accEjecutar.Enabled := LstOriginal.Text ''; end;

El Rinconcito de Delphi

15

end;

El cdigo de respuesta del botn Ejecutar, merece que nos detengamos algo ms, para explicar la lgica que se ha seguido. Normalmente, necesitaremos recuperar un rango de registros de nuestras tablas, lo que implica el uso de parmetros y la necesidad de facilitar la introduccin de los valores para cada uno de esos parmetros por parte del usuario. Para esto, podriamos haber usado la propiedad Params del TIBDataSet, pero para esta ocasin me decid a usar otra estartegia que, en mi opinin, aporta ms posibilidades. Ya sabreis que Interbase ignora las lneas de cdigo que se encuentran delimitadas por los simbolos /* */, es decir, que las toma como comentarios. Aprovecharemos este espacio para introducir informacin acerca de los parmetros que necesita la consulta para ejecutarse. La informacin que nos interesa guardar es: el nombre del parmetro, el tipo de dato y el mensaje para el usuario. Para poder identificar esta informacin dentro de los comentarios, la delimitaremos con { } y dentro de estas, cada campo ir separado por ;. Para identificar los prmetros, estos los he encerrado entre dos interrogaciones. En el listado 2, se puede ver un ejemplo de todo lo dicho.Listado 2. Ejemplo de definicin de una consulta y de sus parmetros. /* Estas lneas son tratadas como comentarios e ignoradas por Interbase, por lo que son el lugar ideal para colocar la definicin de los parmetros 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