09 octubre 2007

Leyendo los metadatos de una tabla de Interbase/Firebird

No hay nada que cause más pereza a un programador que la actualización de campos en las bases de datos de nuestros clientes. Hay muchas maneras de hacerlo: desde archivos de texto con metadatos, archivos SQL o incluso actualizaciones por Internet con FTP o correo electrónico.

Yo lo que suelo hacer es tener un archivo GDB o FDB (según sea Interbase o Firebird respectivamente) que contiene todas las bases de datos vacías. Si tengo que ampliar algún campo lo hago sobre esta base de datos.

Después cuando tengo que actualizar al cliente lo que hago es mandarle mi GDB vacío y mediante un pequeño programa que tengo hecho compara las estructuras de la base de datos de mi GDB vacío y las del cliente, creando tablas y campos nuevos según las diferencias sobre ambas bases de datos.

Ahora bien, ¿Cómo podemos leer el tipo de campos de una tabla? Pues en principio tenemos que el componente IBDatabase tiene la función GetTableNames que devuelve el nombre de las tablas de una base de datos y la función GetFieldNames que nos dice los campos pertenecientes a una tabla en concreto. El problema radica en que no me dice que tipo de campo es (float, string, blob, etc).

LEYENDO LOS CAMPOS DE UNA TABLA

Para leer los campos de una tabla utilizo el componente de la clase TIBQuery situado en la pestaña Interbase. Cuando este componente abre una tabla carga el nombre de los campos y su tipo en su propiedad FieldDefs. Voy a realizar una aplicación sencilla en la cual seleccionamos una base de datos Interbase o Firebird y cuando se elija una tabla mostrará sus metadatos de esta manera:


Va a contener los siguientes componentes:


- Un componente Edit con el nombre RUTADB que va a guardar la ruta de la base de datos.

- Un componente de la clase TOpenDialog llamado AbrirBaseDatos para buscar la base de datos Firebird/Interbase.

- Un botón llamado BExaminar.

- Un componente ComboBox llamado TABLAS que guardará el nombre de las tablas de la base de datos.

- Un componente IBDatabase llamado BaseDatos que utilizaremos para conectar.

- Un componente IBTransaction llamado Transaccion para asociarlo a la base de datos.

- Un componente IBQuery llamado IBQuery que utilizaremos para abrir una tabla.

- Y por último un campo Memo llamado Memo para mostrar la información de los metadatos.

Comenzemos a asignar lo que tiene que hacer cada componente:

- Hacemos doble clic sobre el componente BaseDatos y le damos de usuario SYSDBA y password masterkey. Pulsamos Ok y desactivamos su propiedad LoginPrompt.

- Asignamos el componente Transaccion a los componentes BaseDatos y IBQuery.

- Asignamos el componente BaseDatos a los componentes Transaccion y IBQuery.

- En la propiedad Filter del componente AbrirBaseDatos ponemos:

Interbase|*.gdb|Firebird|*.fdb

- Al pulsar el botón Examinar hacemos lo siguiente:

procedure TFormulario.BExaminarClick( Sender: TObject );
begin
if AbrirBaseDatos.Execute then
begin
RUTADB.Text := AbrirBaseDatos.FileName;
BaseDatos.DatabaseName := '127.0.0.1:' + RUTADB.Text;

try
BaseDatos.Open;
except
on E: Exception do
Application.MessageBox( PChar( E.Message ), 'Error al abrir base de datos',
MB_ICONSTOP );
end;

BaseDatos.GetTableNames( TABLAS.Items, False );
end;
end;

Lo que hemos hecho es abrir la base de datos y leer el nombre de las tablas que guardaremos dentro del ComboBox llamado TABLAS.

Cuando el usuario seleccione una tabla y pulse el botón Mostrar hacemos lo siguiente:

procedure TFormulario.BMostrarClick( Sender: TObject );
var
i: Integer;
sTipo: String;
begin
with IBQuery do
begin
Memo.Lines.Add( 'CREATE TABLE ' + TABLAS.Text + ' (' );

SQL.Clear;
SQL.Add( 'SELECT * FROM ' + TABLAS.Text );
Open;

for i := 0 to FieldDefs.Count - 1 do
begin
sTipo := '';

if FieldDefs.Items[i].FieldClass.ClassName = 'TIBStringField' then
sTipo := 'VARCHAR(' + IntToStr( FieldByName( FieldDefs.Items[i].Name ).Size ) + ')';

if FieldDefs.Items[i].FieldClass.ClassName = 'TFloatField' then
sTipo := 'DOUBLE PRECISION'; // También podría ser FLOAT (32 bits) aunque prefiero DOUBLE (64 bits)

if FieldDefs.Items[i].FieldClass.ClassName = 'TIntegerField' then
sTipo := 'INTEGER';

if FieldDefs.Items[i].FieldClass.ClassName = 'TDateField' then
sTipo := 'DATE';

if FieldDefs.Items[i].FieldClass.ClassName = 'TTimeField' then
sTipo := 'TIME';

if FieldDefs.Items[i].FieldClass.ClassName = 'TDateTimeField' then
sTipo := 'TIMESTAMP';

if FieldDefs.Items[i].FieldClass.ClassName = 'TBlobField' then
sTipo := 'BLOB';

// ¿Es un campo obligatorio?
if FieldByName( FieldDefs.Items[i].Name ).Required then
sTipo := sTipo + ' NOT NULL';

Memo.Lines.Add( ' ' + FieldDefs.Items[i].Name + ' ' + sTipo );

// Si no es el último campo añadimos una coma al final
if i < FieldDefs.Count - 1 then
Memo.Lines[Memo.Lines.Count-1] := Memo.Lines[Memo.Lines.Count-1] + ',';
end;

Memo.Lines.Add( ')' );
Close;
Transaction.Active := False;
end;
end;

Lo que hace este procedimiento es abrir con el componente IBQuery la tabla seleccionada y según los tipos de campos creamos la SQL de creación de la tabla.

Este método también nos podría ser útil para hacer un programa que copie datos entre tablas Interbase/Firebird.

Pruebas realizadas con Firebird 2.0 y Delphi 7.


PUBLICIDAD

lentillas

bajar de peso

bajar peso

2 comentarios:

Anónimo dijo...

HUmm, no se me habia ocurrido, voy a probarlo.

Ademas ya no puedo quejarme creo que aca esta el inicio de la madeja :)

Saludos

StartKill
Lima-Perú

jadel dijo...

Yo utilizo DatabaseComparer de CleverComponent, es freeware. Muestra una especie de Merge con las actualizaciones que va a realizar y te genera un script para actualizar la base de datos target a partir de la master.

Publicidad