Objetivo:

En ésta unidad se tratan algunos temas que se han catalogado como "avanzados", no sólo por su dificultad, sino por que su conocimiento y utilización no es necesario para desarrollar la mayoría de los sistemas de información con Interbase.

Al finalizar la presente unidad, el alumno conocerá a fondo qué es y como se crea de forma correcta una librería de funciones definidas por el usuario (UDF's), la forma de enlazarla con una base de datos, su uso y eliminación.

También conocerá qué son y cómo se usan los filtros Blob, habiendo sido introducido en la utilización y significado de los blobs en interbase.

Por otro lado se comentarán diversas formas de optimizar el rendimiento de las bases de datos, así como de las consultas realizadas desde las aplicaciones cliente al servidor.

Otra de las materias tratadas ofrecerá al alumno un compendio de las diferencias de Interbase entre las plataformas en las que está disponible y una explicación de las cadenas de versión, así como las diversas formas del movimiento masivo de datos en interbase.

 

Panoramica:

La primera parte de la unidad está dedicada a las UDF's. En ella se explican todos los aspectos a tener en cuenta en la creación y utilización de las mismas. No sólo se trata de conocer las posibilidades que ofrecen las UDF's a la hora de crear sistemas de información, sino de ser capaces de crear, por parte del alumno, sus propias funciones.

Para completar el tema, se incluye el código fuente completo de una DLL creada con Delphi, que contiene dos funciones. Aquéllos alumnos que programen con C lo tienen más fácil, ya que Interbase incluye el código fuente en C de una de las dll's de ejemplo.

La segunda parte del capítulo trata de la utilización y comprensión de los filtros blob en interbase. Si pretender profundizar demasiado en la forma de crear un filtro blob, se explica de una forma clara cómo maneja interbase ese tipo de información y los pasos a seguir para su correcto tratamiento.

El tercer apartado del capítulo se dedica a la optimización de consultas al servidor por parte de las aplicaciones cliente.

El resto del capítulo se ocupa de tratar otros temas menores en extensión pero no en importancia, como son el movimiento masivo de datos en interbase, la interpretación de identificadores de cadenas de versión y las diferencias existentes entre las versiones de Interbase existentes para los distintos sistemas operativos. Algunas de estas diferencias ya han sido avanzadas en anteriores capítulos y en éste se ofrecerá una visión global y completa del problema.

 

Funciones definidas por el usuario (UDF's):

Una de las características más sorprendentes de Interbase es la posibilidad que ofrece a los programadores de completar los comandos SQL con funciones adicionales creadas por ellos mismos.

La forma de hacerlo es simple y muy práctica. Las funciones adicionales se crean en una DLL, y posteriormente se declaran en la base de datos.

De esta forma es posible subsanar las carencias propias del lenguaje SQL, como la falta de comandos apropiados para el tratamiento de cadenas de caracteres o de operadores matemáticos avanzados como los de tipo trigonométrico, exponencial o estadístico.

 

Creando una UDF:

Una UDF (User Defined Function) no es más que una función realizada con un lenguaje de programación, que está dentro de una DLL.

Crear una UDF, supone crear una DLL conteniendo una función que toma uno o varios valores y devuelve un resultado. Por supuesto en una misma DLL se pueden introducir todas las UDF's que sea necesario.

A continuación se muestra como crear, en Delphi, una DLL que puede contener cualquier función proggramada por el usuario. Más adelante se pondrá un ejemplo completo con todo el código fuente para crear una DLL que contenga dos funciones. En el ejemplo se ilustrará el tratamiento de cadenas de caracteres y funciones recursivas desde una UDF. En este apartado se pretende ilustrar de forma genérica los pasos a seguir para crear una UDF.

En primer lugar, desde Delphi, se debería iniciar un nuevo proyecto DLL. Para ello en el menú File/New, se escoge el icono correspondiente a una DLL, tal y como se ve en la figura 1.

Figura 1:

 

Posteriormente se debe introducir el código necesario para crear dentro de la DLL las funciones que se vayan a utilizar por el servidor, exportarlas y compilar la DLL.

Como ya se ha comentado, las dll's en las que se pueden crear UDF's son librerías completamente normales, por lo que cualquier herramienta de programación que permita crear Dll's puede ser apropiada para crearlas. En concreto las librerías de ejemplo que acompañan a interbase, y que incluyen el código fuente están programadas en lenguaje C.

No obstante también es relativamente sencillo programar UDF's con Delphi, tal y como se verá a lo largo de esta unidad, y se pueden encontrar más UDF realizadas en Delphi incluyendo el código fuente en la página web de interbase (www.interbase.com ).

 

Cosas que hay que saber para crear UDF's:

En realidad no es necesario saber grandes cosas para poder crear una UDF a la medida de nuestras necesidades. Basta con conocer la técnica para crear DLL, y tener en cuenta algunos detalles adicionales.

En primer lugar, las UDF pueden recibir uno o más valores y devolver otro, pero los valores que recibe la función definida por el usuario están referidos siempre a un registro o tupla.

Es decir, no hay problema en calcular la raiz cuadrada de un número, una subcadena de un cadena de caracteres o cualquier operación de ese tipo; sin embargo no es posible realizar operaciones que afecten a varios registros de una misma tabla, como por ejemplo calcular la media aritmética de los registros de un campo, etc.

Por otro lado es necesario, hasta la versión 5.1 de Interbase, colocar la DLL en el directorio bin de Interbase, ya que sino, el motor de interbase no encuentra la dll.

Contrariamente a lo que se pudiera pensar, el motor de interbase no busca la dll en el mismo directorio en el que se encuentra la base de datos, sino en el directorio de instalación de Interbase. A partir de la versión 5.5, ya se puede especificar en la declaración de la DLL la ruta completa en la que se encuentra la DLL.

 

Registrando la UDF en la Base de datos:

Para registrar una UDF en la base de datos, se utiliza el comando DECLARE EXTERNAL FUNCTION.

 Sintaxis:

DECLARE EXTERNAL FUNCTION nombre [<tipo_de_dato> | CSTRING (entero) [, <tipo_de_dato> | CSTRING (entero) ... ] ] RETURNS {<tipo_de_dato> [BY VALUE] | CSTRING (entero) FREE IT} ENTRY_POINT "<nombre_entrada>" MODULE_NAME "<nombre_dll>" ;

 

En la figura 2 se muestran los argumentos de la función, y una breve descripción de los mismos.

 El argumento <nombre_dll> puede contener la ruta completa de la DLL a partir de la versión 5.5 de Interbase, en las versiones anteriores sólo se especifica el nombre de la DLL, y ésta debe estar colocada en el subdirectorio bin de la ruta de instalación de Interbase.

 

Figura 2:

Argumentos de la función DECLARE EXTERNAL FUNCTION

Argumento

Descripción

Nombre

Nombre de la Función en la base de datos

<tipo_de_dato>

Tipo de dato de los parámetros de entrada o salida:

    • Todos los parámetros de entrada deben ser pasados a la UDF por referencia.
    • El parámetro que devuelve debe ser pasado por valor.
    • No se pueden pasar elementos de un array.

RETURNS

Especifica el valor que devuelve la función.

BY VALUE

Especifica que el valor devuelto se pasará por valor.

CSTRING (entero)

Especifica que el valor devuelto será una cadena "null-terminated", cuya longitud en bytes viene indicada por el parámetro entero.

<nombre_entrada>

Especifica el nombre de la función en la DLL.

<nombre_dll>

Especifica el nombre de la DLL.

 

El nombre de la función en la DLL no tiene porqué ser el mismo que el nombre con el que se declare en la base de datos.

¡Importante!

No es posible utilizar UDF's en los servidores Novell NetWare, al menos hasta las versiones 5.1 y 4.11 de Interbase y NetWare respectivamente..

 

Ejemplo:

El siguiente ejemplo registra la función ib_udf_substr() que se encuentra en la DLL ib_udf.dll en una base de datos, como subcadena().

DECLARE EXTERNAL FUNCTION subcadena

CSTRING(80), SMALLINT, SMALLINT

RETURNS CSTRING(80) FREE_IT

ENTRY_POINT "IB_UDF_substr" MODULE_NAME "ib_udf";

 

¡truco!

Todas las sentencias SQL necesarias para declarar las funciones que contiene la DLL ib_udf.dll, así como una breve descripción de lo que realiza cada una de ellas se encuentra en el archivo ib_udf.sql de la carpeta examples, en el directorio de instalación de interbase..

 

Llamando a la UDF:

Para llamar a una UDF, basta con utilizar la nueva función declarada en la base de datos dentro de una sentencia SELECT, de la misma manera como se utilizan el resto de funciones del lenguaje SQL.

En la figura 3 se puede observar el resultado de una sentencia SELECT utilizando la función subcadena comparado con el resultado que devuelve la cadena de caracteres completa.

 

Figura 3:

 

Eliminar una UDF:

Para eliminar la declaración de una UDF de una base de datos, se debe utilizar el comando DROP EXTERNAL FUNCTION.

 

Sintaxis:

DROP EXTERNAL FUNCTION nombre ;

El comando DROP EXTERNAL FUNCTION elimina la declaración de la función en la base de datos, no la borra físicamente de la DLL, pero la hace inaccesible desde la base de datos.

Sólo están habilitados para borrar la declaración de una UDF:

 

 

Ejemplo de creación de una UDF con Delphi:

A continuación se ofrece el código fuente de una DLL creada con Delphi, que incluye dos UDF's, una llamada Concatenar y otra llamada Factorial.

La primera de ellas devuelve el resultado de concatenar un campo de tipo char con otro de tipo varchar. De ésta forma se ilustra el tratamiento diferenciado que se debe hacer a éstos dos tipos de datos desde la DLL.

La segunda de ellas devuelve el factorial de un entero menor o igual a 100, y mayor o igual a cero. En el caso de pasarle un número que no esté comprendido en ese rango de valores lla función devuelve el código de error -1.

Como se puede observar en el ejemplo, las funciones que contiene la DLL pueden ser recursivas, como ocurre en el caso del cálculo del factorial, o llamar a otras funciones no exportadas de la misma DLL, como sucede en el caso de la concatenación.

 

 Código fuente de la DLL:

library HYagoUDF;
uses SysUtils;
type
{
CONVERSION DE TIPOS
C, C++ -> Delphi -> InterBase
Short = SmallInt; // SmallInt
Long = Longint; // Integer
int = Integer; // Integer
Float = Single; // Float
Double = Double; // Double
*void, *char = PChar; // Char(???), VarChar(???), CString
}

PSmallInt = ^SmallInt;

procedure TrimRight(Cadena: PChar);
var
  PChr: PChar;
begin
  PChr := Cadena + StrLen(Cadena) - 1;
  while (PChr > Cadena) and (PChr[0] = ' ') do
  Dec(PChr);
  PChr[1] := #0;
end;

function Concatenar(CadenaCString, CadenaVarchar: PChar): PChar; cdecl; export;
var
  LongitudCstring, LongitudVarchar: SmallInt;
  Resultado: Array [0..255] of Char;
begin
//Es necesario eliminar los espacios en blanco que pueda tener por ser un Char
  TrimRight(CadenaCString);
  LongitudCstring := StrLen(CadenaCString);
// El 1º y 2º bytes de un VarChar contienen la longitud del VarChar
  LongitudVarchar := PSmallInt(CadenaVarchar)^;
  StrCopy(Resultado + 2, CadenaCString);
  StrLCat(Resultado + 2, CadenaVarchar + 2, LongitudCstring + LongitudVarchar);
// Se define la longitud del varchar
  PSmallInt(@Resultado)^ := LongitudCstring + LongitudVarchar;
  Result := Resultado;
end;
  
function Factorial(var n: SmallInt): Double; cdecl; export;
var
  n_1 : SmallInt;
begin
  n_1 := n - 1;
  if (n < 0) or (n > 100) then
    Result := -1.0
  else if n = 0 then
         Result := 1.0
       else
         Result := Factorial(n_1) * n;
end;

exports
  Concatenar,Factorial;

begin
end.

 

 

Declaración de las funciones en la base de datos:

declare external function Concat cstring(255), varchar(255) returns varchar(255) entry_point "Concatenar" module_name "HYagoUDF";

 

declare external function Factorial Smallint returns double precision by value entry_point "Factorial" module_name "HYagoUDF";

 

 

Filtros BLOB:

Un blob es un tipo de dato cuyo tamaño puede variar de forma dinámica y del que no se especifica tamaño y codificación. Se pueden utilizar para almacenar grandes cantidades de datos de diferentes tipos, entre los que se incluyen:

Los datos almacenados en un tipo de dato blob se pueden manipular de forma similar a como se tratan los datos almacenados en cualquier otro tipo de dato. Interbase almacena los datos de tipo blob dentro de la base de datos, en contraste con otros sistemas que utilizan punteros a ficheros externos a la base de datos. Para cada blob, existe un único identificador en la tabla a la que pertenece, que apunta al lugar físico en el que se encuentra el blob. Al almacenar los blobs dentro de la base de datos, interbase impide el acceso y modificación desde el exterior.

La combinación de una total administración de los blobs por parte de la base de datos con la posibilidad de almacenar diferentes tipos de datos en los blobs, hacen de Interbase el soporte ideal para aplicaciones de tipo multimedia. Por ejemplo, Interbase es una excelente plataforma para aplicaciones interactivas de almacenamiento de documentos como enciclopedias, que pueden contener miles de descripciones, fotografías o videoclips.

 

En realidad, un blob es un tipo de dato de Interbase que representa varias realidades diferentes, por lo que la creación del dato y su primer almacenamiento debe realizarse con el software apropiado, grabándolo en un archivo del tipo que corresponda ( .TIFF, .BMP, .WAV, .AVI, .DOC, etc..).

Por otro lado, para manejar diferentes tipos de datos, se han creado diferentes subtipos de blobs. Aparte de los tipos de datos predefinidos en Interbase, numerados del 0 al 6, el usuario puede definirse sus propios subtipos de dato, a los que debe asignar números negativos (desde el -1 hasta el -32.678). En la figura cuatro se pueden ver los subtipos blob predefinidos de Interbase y el tipo de dato que puede almacenar cada uno de ellos.

 

Figura 4:

Suptipos de dato Blob predefinidos en Interbase

Subtipo

Descripción

0

No estructurado, generalmente aplicado a tipos de dato binarios o indeterminados.

1

Texto.

2

BLR, Binary language representation.

3

Lista de control de acceso.

4

Reservado para su uso en el futuro.

5

Descripción codificada de los metadata.

6

Descripción de transacciones multi-base de datos que finalizan de forma irregular.

  

Como normalmente los datos que se almacenan en los blobs suelen ocupar bastante espacio, Interbase almacena los blobs de una forma más eficiente por medio de la técnica de la segmentación. Evidentemente, sería mucho menos eficiente almacenar cada blob en el disco de forma contigua. Así, cada Blob es almacenado en una serie de segmentos indexados por un manejador que interbase genera cuando se crea un blob, conocido como Blob ID.

Un filtro Blob es una rutina que traslada datos blob de un subtipo a otro. Interbase incluye un conjunto de filtros blob internos, que convierten del subtipo 0 al subtipo 1 (texto) y viceversa. Además de éstos filtros estándar, es posible escribir nuevos filtros externos para realizar traslados de datos de una forma especial. Por ejemplo, es posible desarrollar un filtro blob, para convertir ficheros en formato bitmap a otro formato diferente.

 

¡Importante!

Los filtros Blob están disponibles en todas las plataformas en las que se puede instalar Interbase, excepto en Novell NetWare (hasta la versión 4.11), donde no pueden ser ni creados ni usados..

 

Los filtros estándar de Interbase convierten los datos blob del subtipo 0 o cualquier tipo del sistema de interbase, al subtipo 1 (Texto).

Cuando un filtro de texto está siendo usado para leer datos de una columna de tipo blob, éste modifica el comportamiento normal de Interbase para suplir los segmentos. No tiene en cuenta la naturaleza de los segmentos en la columna de tipo blob, y separa los datos por el carácter de retorno de carro (\n).

El filtro blob devuelve los primeros caracteres incluyendo el primer retorno de carro como el primer segmento, los siguientes incluyendo el segundo retorno de carro como el segundo segmento, etc..

 

¡truco!

Para convertir cualquier subtipo no texto a Texto, declare el subtipo de la cláusula FROM como subtipo 0 y el subtipo de la cláusula TO como subtipo 1.

 

Al contrario que los filtros estándar de Interbase que conmutan entre el subtipo 0 y el 1, un filtro blob externo forma parte de una librería de rutinas creadas por el usuario (DLL).

Para usar un filtro externo, primero es necesario escribirlo, compilarlo y linkarlo, para después declararlo en la base de datos que contenga los datos blob que se van a tratar.

 

Declarando un Filtro BLOB:

Para declrar un filtro blob se utiliza el comando DECLARE FILTER.

Sintaxis:

DECLARE FILTER filtro INPUT_TYPE subtipo OUTPUT_TYPE subtipo
ENTRY_POINT "nombre_entrada" MODULE_NAME "nombre_dll" ;

 

Figura 5:

Argumentos de la función DECLARE FILTER

Argumento

Descripción

Filtro

Nombre del Filtro en la base de datos. No pueden existir nombres de filtros repetidos dentro de una misma base de datos.

INPUT_TYPE subtipo

Especifica el tipo de dato desde el que se va a realizar la conversión.

OUTPUT_TYPE subtipo

Especifica el tipo de dato al que se va a realizar la conversión.

"nombre_entrada"

Especifica el nombre del filtro en la DLL.

"nombre_dll"

Especifica el nombre de la DLL.

 

Una vez el filtro está declarado en la base de datos, ya es posible trabajar con él desde cualquier aplicación.

 

Llamando al filtro BLOB:

Para trabajar con filtros blob, existen 5 comandos SQL que sirven para preparar, abrir, leer, escribir y cerrar un dato blob. Sin embargo estos comandos SQL no están disponibles ni en Wisql ni en isql, por lo que sólo se pueden utilizar como sql embebido en las aplicaciones.

Otra forma de poder trabajar con filtros blob desde una aplicación si no se dispone del preprocesador en el lenguaje en el que se está desarrollando (la licencia para el preprocesador en lenguaje Pascal no está incluída normalmente en las licencias habituales de servidor) es trabajar directamente con las funciones del API de interbase que existen para ello.

Declare Cursor:

Se utiliza para declarar un cursor que lea o inserte un blob. Se debe declarar un cursor para cada columna de tipo blob que se desee manejar.

DECLARE cursor CURSOR FOR { READ BLOB columna FROM tabla | INSERT BLOB columna INTO tabla } [FILTER [FROM subtipo ] TO subtipo ] [MAXIMUM_SEGMENT longitud ] ;

 

Figura 6:

Argumentos de la función DECLARE CURSOR

Argumento

Descripción

Cursor

Nombre del Cursor.

Columna

Nombre de la columna.

Tabla

Nombre de la Tabla.

READ BLOB

Declara una operación de lectura sobre el blob.

INSERT BLOB

Declara una operación de escritura sobre el blob.

[FILTER [FROM subtipo ] TO subtipo ]

Especifica el filtro que se utilizará para trasladar el blob de un formato a otro y que viene determinado por el subtipo.

MAXIMUM_SEGMENT longitud

Longitud de la variable local que recibirá los datos del blob después de cada FETCH.

 

 Open Cursor:

Se utiliza para abrir un cursor blob declarado previamente y prepararlo para leer o insertar.

OPEN [TRANSACTION nombre ] cursor {into | using } :blob_id ;

 

Figura 7:

Argumentos de la función OPEN CURSOR

Argumento

Descripción

TRANSACTION name

Especifica la transacción que manejará el cursor. Es opcional.

Cursor

Nombre del cursor.

INTO

Para cursores que realizan operaciones de escritura.

USING

Para cursores que realizan operaciones de lectura.

Blob_id

Identificador de la columna Blob.

  

Insert Cursor:

Se utiliza para escribir datos blob en una columna de tipo blob.

Para utilizarlo, previamente se debe haber inicializado la variable local que contendrá los datos a insertar, y conocer su longitud. También se debe haber declarado el cursor y éste debe estar abierto.

INSERT CURSOR cursor VALUES ( :buffer [ INDICATOR ] :longitud);

  

Figura 8:

Argumentos de la función INSERT CURSOR

Argumento

Descripción

Cursor

Nombre del cursor.

:buffer

Nombre de la variable que contiene los datos a insertar.

:longitud

Longitud, en bytes, de la variable buffer.

 

 Fetch Cursor

Se utiliza para obtener el siguiente segmento disponible de una columna blob y almacenarlo en una variable.

FETCH CURSOR cursor INTO ( :buffer [ INDICATOR ] :longitud);

Este comando puede devolver dos códigos , el 100 indica que no hay más segmentos para traer, y el 101 indica que el segmento ha sido almacenado en buffer.

  

Figura 9:

Argumentos de la función FETCH CURSOR

Argumento

Descripción

Cursor

Nombre del cursor.

:buffer

Nombre de la variable en la que se almacenarán los datos del blob.

:longitud

Variable usada para almacenar el número de bytes devueltos por la función fetch.

 

 Close cursor:

Se utiliza para cerrar el cursor.

CLOSE cursor_blob ;

El argumento cursor_blob identifica a un cursor por su nombre.

Al cerrar el cursor, Interbase libera los recursos asociados al mismo. Este comando se utiliza cuando se han leído todos los segmentos de un blob, o cuando se han escrito todos los segmentos de un blob.

En las figuras 10 y 11 se muestran dos ejemplos de utilización de cursores para un blob, con SQL embebido.

 

 Figura 10:

 

EXEC SQL

DECLARE cursor1 CURSOR FOR READ BLOB

Campo1 FROM tabla1 FILTER FROM 0 TO 1

MAXIMUM_SEGMENT 40 ;

EXEC SQL

OPEN cursor1 USING :blob_id;

While (SQLCODE <> 100) or (SQLCODE = 101) do begin

EXEC SQL

FETCH cursor1 INTO :linea :long ;

End ;

EXEC SQL

CLOSE cursor1 ;

 

  

Figura 11:

 

EXEC SQL

DECLARE cursor2 CURSOR FOR INSERT BLOB

Campo2 INTO tabla2 FILTER 1 TO 0;

EXEC SQL

OPEN cursor2 INTO :blob_id;

EXEC SQL

INSERT CURSOR cursor2 VALUES (:linea INDICATOR :longi) ;

EXEC SQL

CLOSE cursor2 ;

 

Para que funcione correctamente, previamente el filtro debe estar declarado en la base de datos.

 

Como ya se ha comentado con anterioridad, también se puede utilizar el API de Interbase como alternativa al SQL embebido. En la figura 12 se describen las principales funciones del API para el tratamiento de blobs.

 

Figura 12:

Funciones del API para el tratamiento de blobs

Función API

Descripción

Isc_blob_default_desc()

Lee la información de un descriptor de blob.

Isc_blob_gen_bpb()

Genera el BPB (Blob Parameter buffer).

Isc_blob_blob_info()

Devuelve información acerca de un blob abierto.

Isc_blob_lookup_desc()

Determina las características de un blob, a partir del nombre de la tabla y columna.

Isc_blob_set_desc()

Inicializa un descriptor de blob.

Isc_cancel_blob()

Descarta un blob.

Isc_close_blob()

Cierra un blob abierto.

Isc_create_blob2()

Crea y abreun blob para escritura. Se puede especificar un filtro blob para transformaciones de un subtipo a otro.

Isc_get_segment()

Obtiene datos de una columna blob en las filas devueltas por un select.

Isc_open_blob2()

Abre un blob. Se puede especificar un filtro blob para transformaciones de un subtipo a otro.

Isc_put_segment()

Escribe datos en un blob.

 

  

Optimización de queries y ajuste de rendimiento:

Una de las cuestiones más importantes a la hora de trabajar en entornos Cliente/Servidor, en los que toda la lógica del sistema de información se encuentra en el servidor, es la optimización de consultas al servidor por parte de los clientes.

Si un cliente necesita un campo de una tabla y para obtenerlo realiza una consulta en la que se seleccionan todos los campos de la tabla, la consulta no es óptima en cuanto a cardinalidad.

Si un cliente necesita un registro de una tabla y para obtenerlo realiza una consulta en la que se seleccionan todos los registros de la tabla, entonces la consulta no es óptima en cuanto a selectividad.

Para evitar una ralentización innecesaria del sistema, es conveniente estar atento a estas cuestiones.

Otra herramienta que Interbase ofrece para optimizar el rendimiento de las bases de datos es la utilización de índices en determinadas ocasiones.

Por último, se pueden aprovechar los procedimientos almacenados para realizar consultas complejas o de gran tamaño, ya que generalmente se suele ganar en velocidad.

  

Controlando el grado y la cardinalidad:

Utilizaremos el término grado para referirnos al número de columnas que se obtienen en una determinada consulta. De la misma forma, utilizaremos el término cardinalidad para referirnos al número de registros que se obtienen al realizar una consulta.

No es necesario explicar, que el producto de grado y cardinalidad representa el número de datos individuales que devuelve una consulta.

Imaginemos una tabla llamada Tabla1 con 20 columnas de tipo integer y 100.000 de registros. La sentencia SELECT * FROM TABLA1 devolvería todos los registros de la tabla, es decir 20*100.000*32 bits = 640.000.000 bits = 7.812,5 Kb = 7,63 Mb.

No es nada bueno para el rendimiento de un sistema de información el realizar una consulta que devuelva casi 8 Megas de información, no sólo por el tráfico por la red que supone, sino también porque el cliente debe recibir y manejar esa información, una vez realizada la consulta.

Es evidente que para disminuir el tamaño de la consulta, se pueden realizarr cambios que afecten a los tres operandos que intervienen en el cálculo del mismo.

El primer operando es el que afecta al grado. Para disminuir el grado de una consulta, se deben incluir en la cláusula SELECT de la consulta únicamente los campos imprescindibles. Por tanto sólo es recomendable el uso de SELECT * cuando sea imprescindible consultar todos los campos de la tabla.

 Esta cuestión cobra mayor relevancia cuando la consulta se realiza mediante un join de varias tablas. En ese caso es muy recomendable enumerar todos los campos necesarios de cada una de las tablas implicadas, y olvidarse del cómodo asterisco.

El segundo operando se refiere al número de registros que intervienen en la consulta. Para disminuir éste operando el lenguaje SQL ofrece la cláusula WHERE. Condicionando el resultado de una consulta por medio de la cláusula WHERE es posible disminuir de una forma importante el número de registros y por tanto optimizar en gran medida la consulta.

En realidad la consulta SELECT * FROM Tabla1 nunca debería ser utilizada, ni siquiera en consultas sobre tablas que a priori el programador cree que siempre serán pequeñas.

Lo ideal es realizar consultas que obtengan únicamente el número de campos y de registros mínimo para cada operación a realizar, ya que es la única forma de que los sistemas de información sean realmente tan ágiles como sea posible.

El tercer operando es el que afecta al tamaño del dato almacenado en cada campo. En el ejemplo se ha utilizado el tipo de dato integer que como sabemos en Interbase tiene un tamaño de 32 bits.

Si se tratara de un double precision el tamaño sería precisamente el doble, 64 bits, por lo que en este sentido se puede recomendar utilizar siempre el tipo de dato más pequeño, pero eso sí, que se adapte a nuestras necesidades.

De los tres operandos que intervienen en el cálculo del tamaño de una consulta, en el que menos se hace incapié en éste capítulo es en el tercero, el que se refiere al tipo de dato, ya que es un tema que no se puede solucionar al realizar consultas, sino al diseñar la base de datos.

 

Trabajando con indices:

En los casos en los que se realizan con relativa frecuencia consultas que afectan a un mismo índice, es conveniente de vez en cuando recalcular la selectividad de ese indice.

Como ya sabemos, para recalcular la selectividad de un índice se utiliza el comando SET STATISTICS INDEX indice ;

Utilizar el comando de forma periódica sobre aquéllos índices que se utilizan con mayor frecuencia, también puede mejorar el rendimiento de la base de datos.

Otra forma de optimizar consultas por medio de índices es indicándole al motor de la base de datos el plan que debe utilizar para realizar la consulta.

Interbase siempre utiliza un plan para realizar las consultas, determinando en cada una de ellas cuál es el más eficiente por medio de su optimizador interno.

Si el programador le indica al motor de la base de datos el plan a utilizar, entonces no hará falta ejecutar el optimizador, con lo que se puede ganar algo de tiempo.

 En éste sentido es necesario advertir que aunque en condiciones normales el optimizador será capaz de encontrar el plan más apropiado para resolver la consulta realizada, no siempre tiene por qué ser así, ya que el optimizador está diseñado partiendo del compromiso con el rendimiento global del sistema, es decir, no se puede perder más tiempo en optimizar la consulta del que se gana en dicha optimización.

Por tanto, si bien en la mayoría de las ocasiones bastará con realizar la consulta, ver el pan utilizado por el motor para resolverla y luego incluirlo en la sentencia SQL, pueden existir consultas en las que el utilizar planes alternativos a los del sistema sea conveniente e incluso necesario.

  

Trabajando con STORED PROCEDURES:

Otra de las formas de mejorar el rendimiento en consultas complejas pero habituales,consiste en implementarlas como storec procedures.

Como sabemos, un procedimiento almacenado que devuelve valores puede ser llamado desde una sentencia SELECT, tal y como se puede apreciar en las figuras 13 y 14.

  

Figura 13:

create procedure detalles_articulo_des (BUSCA varchar(60))

returns (ARTICULO varchar(10), TITULO varchar(60))

as begin

for select articulo,titulo

from art_articulos where titulo like :busca

into :articulo,:titulo do suspend ;

end

 

 La definición del procedimiento almacenado que aparece en la figura 13 pertenece a uno de los que incluye la base de datos del curso.

En él se observa como realiza una busqueda aproximada en la tabla artículos en función del parámetro de entrada BUSCA.

La forma de utilizarlo en un select es la que se presenta en la figura 14.

  

Figura 14:

 

La ventaja de utilizar este tipo de stored procedures radica en que en ocasiones la petición al servidor es mucho más sencilla de interpretar por el intérprete de comandos, lo cual ya supone un avance, pero sobre todo en el hecho de que la consulta, al estar compilada, no necesita de procesamiento previo, ya que éste se realizó al compilar el stored procedure.

El procesamiento de consultas de esta forma un tanto singular pero muy efectiva, es otra forma de optimizar el rendimiento de las bases de datos.

 

La arquitectura multi-generacional:

Entre los conceptos avanzados a tratar en esta unidad, uno de los más intrigantes es el de la arquitectura multigeneracional.

La arquitectura multigeneracional es una forma de organización interna de Interbase por la cual el motor de la base de datos puede crear varias versiones de un dato en la misma base de datos.

Cada transacción sólo tiene acceso a una versión de cada dato, de forma que la base de datos aparece como consistente para todas las transacciones activas en cada momento.

Sin embargo, un mismo dato puede tener valores diferentes para transacciones diferentes, dependiendo del nivel de aislamiento de cada una de ellas con respecto a las otras, las modificaciones que hayan podido realizarse en la base de datos, y los commits o rollbacks pendientes.

Las diferentes versiones de datos no son controlables por el usuario, ya que la base de datos debe aparecer de forma consistente para las transacciones activas. Es decir, para cada transacción sólo existe una versión de cada dato, que es la que ella puede leer y modificar.

Los diferentes niveles de aislamiento, su significado y forma de utilización, ya fueron estudiados en la unidad 10.

  

Moviendo Datos en Interbase:

Cuando se habla de movimiento masivo de datos, se pueden distinguir varias posibilidades:

En realidad los tres últimos casos nos presentsn el mismo tipo de problema, ya que en esos casos Interbase carece de herramientas propias adecuadas para tratar el problema.

 

Moviendo datos dentro de una misma base de datos:

Para el traslado simple de datos desde una tabla a otra, el lenguaje SQL admite la combinación de las sentencias SELECT e INSERT, permitiendo insertar en una tabla el resultado de un select sobre una o varias tablas.

 

Ejemplo:

INSERT INTO Clientes(Cod_cliente, nombre, direccion)

SELECT cod_proveedor, nombre, direccion FROM Proveedores;

INSERT INTO Clientes(Cod_cliente, nombre, direccion,telefono)

SELECT P.cod_proveedor, P.nombre, P.direccion, T.Telefono1

FROM Proveedores P JOIN Telefonos T on P.Cod_Proveedor = T.codigo;

 

En el supuesto de que la inserción masiva o el traslado de datos involucre a más de una tabla tanto para la lectura como para la inserción de datos, lo más recomendable es crear procedimientos almacenados en la base de datos, en la medida de lo posible.

  

Ejemplo:

 

Create procedure traslado

As

Declare variable v_codigo varchar (5) ;

begin

For select cod_cliente from Clientes into :v_codigo do

Begin

If ( :v_codigo like '5%' ) then

insert into contactos(cod_contacto, nombre, direccion)

select(cod_cliente, nombre, direccion) from clientes

where cod_cliente = :v_codigo ;

Else if ( :v_codigo like '4%' ) then

...

ELse ...

End

End

 

Existe entre no pocos programadores la tendencia a manejar este tipo de movimiento de datos desde la aplicación cliente, olvidándose de las ventajas en cuanto a rendimiento y prestaciones que ofrece el trabajar precisamente al contrario, realizando y manejando las operaciones totalmente desde el servidor.

  

Moviendo datos dentro de un mismo servidor:

Cuando se trata de mover datos entre dos bases de datos, lo normal es realizar el traslado de la información a través de un programa externo. Existen programas estándar en el mercado, como el database desktop y otros, que están capacitados para realizar ese tipo de tareas, aunque no siempre de la manera más óptima.

En realidad, existen tres posibles caminos:

 

La primera opción tiene la ventaja de que si el programa está hecho a medida, es posible optimizar el traspaso, realizar todo tipo de comprobaciones y conversiones, etc..

 La segunda opción tiene la ventaja de que el traspaso se puede realizar utilizando comandos SQL en las dos bases de datos, sin necesidad de utilizar herramientas externas al motor de bases de dattos de Interbase, pero arrastra todos los inconvenientes y limitaciones de la utilización de tablas externas en Interbase, además del peligro que supone para la seguridad del sistema el conservar datos en ficheros de texto..

La última opción, es un híbrido entre las dos anteriores, ya que mientras para la obtención del script es necesaria la utilización de un programa externo (ya sea estándar como Marathon, o hecho a medida), para la ejecución del script se pueden utilizar las herramientas de interbase, o un programa externo. El inconveniente de ésta opción es que un script demasiado largo puede dar problemas, y además los datos se ubican de forma temporal en un fichero de texto, por lo que se pone en peligro la confidencialidad de los mismos.

 

Moviendo datos entre diferentes tipos de bases de datos:

Para traspasar datos entre diferentes plataformas de bases de datos, se necesita construir un sistema de traspaso que puede ser tan simple como un programa que se conecte a los dos sistemas de información y realice el traspaso de información, o tan complicado como varios programas que recojan,envíen y reciban información y sean capaces de comunicarse entre sí, enviando los datos encriptados, etc..

 En principio lo habitual es tener que traspasar datos de un sistema de información basado en tablas panas a una base de datos de interbase o viceversa, así como exportar datos de interbase a un formato de datos reconocible por las diferentes hojas de cálculo o procesadores de texto del mercado.

Para éstas tareas sencillas se pueden utilizar pequeños módulos o programitas de exportación o importación de datos, que recorran los registros del fichero de entrada o la tabla a exportar, y vayan procesando la información por medio de un bucle.

Si lo que se pretende es exportar datos para ser leídos por una hoja de cálculo, lo más cómodo es hacerlo en formato CSV o TXT,aunque también es adecuado el formato dbf y otros. En definitiva, lo importante es realizar la exportación de la forma más sencilla posible intentando no perder compatibilidad con los diferentes productos existentes en el mercado.

 

Diferencias entre plataformas:

Como sabemos, Interbase es un motor de bases de datos multi-plataforma, es decir, puede funcionar de la misma manera en varios sistemas operativos diferentes. O casi, porque existen pequeñas diferencias entre ellos que se van a analizar en este apartado.

Distinguiremos tres grandes grupos de sistemas operativos, por un lado los sistemas oprerativos Microsoft, es decir Windows 95/98 y NT, por otro lado los sistemas operativos UNIX, entre los que se encuentran los diferentes Linux, Solaris, HP-UX, etc., y por último el sistema operativo Novell NETWare.

 

La instalación:

La primera diferencia evidente entre las tres plataformas se encuentra en la instralación. La instalación se realiza en cada plataforma de la forma estándar, y es evidente que cambia radicalmente de un sistema operativo a otro.

 

La gestión de ususarios:

Mientras que en los sistemas operativos Windows y Novell la gestión de usuarios y permisos en la base de datos se reduce a las herramientas que ofrece el SQL, en sistemas operativos UNIX la gestión se encuentra más integrada en el sistema operativo.

En los sistemas UNIX los usuarios creados en el sistema operativo son los mismos que luego se utilizarán para acceder al servidor de interbase, frente al resto de sistemas operativos, en los que los usuarios definidos en el servidor de Interbase son independientes de los definidos en el sistema operativo.

 

Las Udf's:

Otra de las diferencias importantes entre plataformas,es la limitación que tiene Interbase para Novell al no soportar las Udf's.

Este punto es importante ya que un sistema de información que las utilice no podrá implementarse de forma adecuada en un servidor Novell.

 

Los filtros blob:

Al igual que pasa con las Udf's, los filtros blob tampoco están disponibles en Interbase para Novell.

En realidad no es posible utilizar en los servidores Novell aquéllas funcionalidades que implican ejecución de procesos ajenos al motor de interbase, como es el caso de las udf y los filtros blob.

 

Las herramientas administrativas del sistema:

Interbase provee una serie de herramientas para administrar el servidor de interbase, que también varían en las diferentes plataformas.

Es evidente que las herramientas tipo windows sólo se pueden utilizar bajo los sistemas operativos windows 95/98 y NT, aunque desde un cliente windows se puede administrar sin problemas un servidor de cualquier otro sistema operativo con las citadas herramientas.

 

Los protocolos de comunicaciones:

Interbase soporta tres tipos de protocolo de comunicaciones: TCP/IP, Novell SPX y NetBeui.

El protocolo TCP/IP está disponible en las plataformas Windows 95/98, NT y los diferentes Unix.

El protocolo Novell SPX es específico de redes novell y sólo se utiliza para servidores Novell.

El protocolo NetBeui sólo está disponible para servidores Windows NT. Es importante destacar que NO está disponible en Interbase para servidores Windows 95/98.

Sin embargo, los clientes de un servidor Windows NT, pueden utilizar el protocolo NetBeui aún siendo Windows95/98.

 

Interpretando Identificadores de versiones:

 Cada versión de Interbase tiene su propio identificativo de versión. Para saber la versión que está instalada en un determinado servidor, sepuede utilizar el comando SHOW VERSION , conectarse al servidor desde el Interbase Server Manager para windows o bien desde el Interbase Windows ISQL desde el menú Session pulsar en el item Display Connect Version. El resultado es el que se puede ver en la figura 15

 

Figura 15:

SHOW VERSION

ISQL Version: WI-V5.1.1.680

InterBase/x86/Windows NT (access method), version "WI-V5.1.1.680"

on disk structure version 9.0

 

El identificador de versión se encuentra en la penúltima línea y es la cadena de caracteres entrecomillada que aparece tras la palabra version.

En el ejemplo de la figura 15 el identificador de versión es: WI-V5.1.1.680.

La cadena de caracteres está formada por dos partes fundamentalmente, separadas por un guión. En la primera parte se indica la plataforma, y en la segunda parte la versión, que comienza siempre por la letra V. La versión a su vez se compone de cuatro partes, el número de versión o build, los números de revisión mayor y menor y el número de build de la revisión actual.

La cadena de identificación de la versión coincide en Windows con la versión del archivo ibserver.exe y del archivo ibguard.exe

 

Plataforma:

Cada plataforma de interbase tiene su propio identificador de plataforma. A continuación se muestran algunos:

WI

Windows 95/98 y Windows NT

LIS

Linux

NW

Novell

HP

HP Unix

 

Build y números de revisión:

El número a continuación de la letra V representa la versión de Interbase, y el resto de números de la cadena representan revisiones o actualizaciones que se hayan podido realizar dentro de la misma versión, sobre todo para ir corrigiendo los diferntes bugs que contuviese la versión inicial.

Por ejemplo, la versión inicial de interbase 5 que apareció en el mercado, interbase 5.0 para windows tenía la cadena de versión WI-V5.0.0.253, mientras que la cadena de la primera actualización, Interbase 5.1 es WI-V5.1.1.680

  

PREGUNTAS:

  1. ¿En qué sentido se puede perder compatibilidad al utilizar en un sistema de información UDF´s o filtros Blob?
  2. Indicar tres formas de mejorar la velocidad en una consulta compleja de una aplicación cliente a un servidor de Interbase.
  3. ¿Cáles son las dos partes principales de la cadena de identificación de versión, y qué contiene cada una de ellas?.

 

EJERCICIO:

Crear una DLL con tres funciones definidas por el usuario, en el lenguaje de programación que el alumno prefiera. Después se enlazará en la base de datos del curso, oo en otra base de datos escogis¡da por el alumno.

La DLL deberá contener tres UDF`s, que servirán para realizar las siguientes tareas respectivamente:

 

RESPUESTAS A LAS PREGUNTAS DE LA UNIDAD 10:

 

  1. ¿Qué es una transacción?
  2. La ejecución de un programa que incluye operaciones de acceso a la base de datos se denomina transacción de base de datos, o simplemente transacción.

     

  3. ¿ Qué comportamiento es especificado cuando se utiliza la transacción por defecto?.
  4. Si se utiliza SET TRANSACTION sin parámetros, se inicia la transacción por defecto, y se produce el siguiente comportamiento:

    READ

    WRITE

    WAIT

    ISOLATION LEVEL SNAPSHOT.

     

  5. ¿ Como se finaliza una transacción?

Cuando la transacción ha finalizado todas sus operaciones, la transacción debe de terminar, o un error avisa de que se complete la transacción, ya que la transacción debe dejar la base de datos en un estado consistente. Hay dos operaciones para terminar una transacción:

 

INDICE

Objetivo.

Panorámica.

Funciones definidas por el usuario.

Creando una UDF.

Cosas que hay que saber para crear UDF's.

Registrando la UDF en la base de datos.

Llamando a la UDF.

Eliminar una UDF.

Ejemplo de creación de una UDF con Delphi.

Filtros Blob.

Declarando un filtro Blob.

Llamando al filtro blob.

Optimización de queries y ajuste de rendimiento.

Controlando el grado y la cardinalidad.

Trabajando con índices.

Trabajando con stored procedures.

La arquitectura multigeneracional.

Moviendo datos en Interbase.

Moviendo datos dentro de una misma base de datos.

Moviendo datos dentro de un mismo servidor.

Moviendo datos entre diferentes tipos de bases de datos.

Diferencias entre plataformas.

La instalación.

La gestión de usuarios.

Las UDF's.

Los filtros Blob.

Las herramientas administrativas del sistema.

Los protocolos de comunicaciones.

Interpretando identificadores de versiones.

Plataforma.

Build y números de revisión.

Preguntas

Ejercicio

Respuestas a las preguntas de la unidad 10