Introducción a las tareas de Administración

2 comentarios

Aunque una bien programada Base de Datos de Firebird prácticamente no necesita de mantenimiento y puede usarse 24/7 (o sea: durante las 24 horas, los 7 días de la semana) sin problemas, siempre hay algunas tareas que se deben realizar con ella. A la persona que realiza dichas tareas se la conoce como “Administrador de la Base de Datos” y puede ser una persona distinta del programador o desarrollador porque sus tareas son muy diferentes.

Las tareas del Administrador son las siguientes:

  • Elegir la versión del Firebird que se instalará
  • Instalar el Servidor del Firebird
  • Determinar el alias que tendrá la Base de Datos
  • Parametrizar el archivo FIREBIRD.CONF
  • Analizar las estadísticas de la Base de Datos
  • Realizar los backups y los restores

Por lo tanto, el Administrador debe conocer:

  • Las características de cada versión del Firebird
  • Las diferencias entre esas versiones
  • La arquitectura MGA
  • El significado de las estadísticas de la Base de Datos
  • Las ventajas y desventajas de cada manera de realizar los backups y los restores
  • Que es y como funciona la recolección de basura

Lo ideal es detectar los problemas potenciales que podrían ocurrir antes de que realmente ocurran, para evitar dolores de cabezas. Para eso es de una gran ayuda analizar las estadísticas porque ellas nos dirán que hace el Servidor y cuando.

La arquitectura MGA (multigeneracional) usa varias versiones de cada registro para que los lectores no interfieran con los escritores ni los escritores con los lectores. Gracias a esta arquitectura, si un usuario se arrepiente de lo que estaba haciendo o si se detecta un error, se puede regresar la Base de Datos al estado consistente anterior. Sin embargo, la arquitectura MGA tiene una desventaja: puede ser causa de un bajo rendimiento cuando las transacciones están mal programadas. Analizando las estadísticas se pueden encontrar esas malas transacciones.

Otra ventaja de la arquitectura MGA es que los usuarios pueden continuar trabajando mientras se realiza un backup. O sea que no es necesario realizar los backups durante la hora del almuerzo o a la medianoche, como con otros sistemas.

En el archivo de backup se copian las definiciones de los índices, pero no los índices. Cuando se realiza la restauración esos índices son regenerados y todos los parámetros de las transacciones son reiniciados.

Para obtener un mejor rendimiento, lo aconsejable es que el backup se realice en otra computadora. Para comprobar que se tiene un backup válido hay que verificar que la fecha y la hora de la Base de Datos original sean diferentes a la fecha y la hora del backup y también restaurar el backup en otra computadora. Si la restauración se realizó con éxito, entonces se puede confiar en esa copia.

El backup se puede automatizar, para que se realice todos los días a determinada hora (o inclusive, varias veces cada día). Esto puede hacerse mediante las “tareas programadas” del Windows o con programas especializados.

La recolección de basura depende de un correcto manejo de las transacciones. Lo que se recolecta son datos y páginas de índices y el Firebird puede realizar esa tarea automáticamente. Cuando se recolecta la basura:

  • Se limpia la Base de Datos
  • Se reorganiza el espacio de memoria
  • La limpieza se ejecuta en segundo plano, sin interferir con las operaciones de los usuarios

Si no se recolecta la basura, el rendimiento de la Base de Datos se irá degradando poco a poco, hasta que llegará el momento en que realizar cualquier operación con ella tomará demasiado tiempo.

Resumen de tareas administrativas

  • Ninguna tarea administrativa es requerida si la Base de Datos está bien programada
  • Si el manejo de las transacciones es el correcto, ninguna tarea de mantenimiento se necesita
  • La recolección de basura depende de un manejo correcto de las transacciones
  • La recolección de basura afecta a los datos y a las páginas de índices

 

Generando códigos dependientes

3 comentarios

A veces podemos encontrarnos con la siguiente situación: debemos codificar a los ítems pero esos códigos deben empezar con algunos caracteres predeterminados.

O sea, podríamos tener esta clase de códigos: AAC00001, AAC00002, AAC00003, CAM00001, CAM00002, ESC00001, ESC00002, ESC00003, etc.

Como ves, algunos códigos empiezan con AAC, algunos con CAM, algunos con ESC, etc. Y en cada caso la numeración empieza con 00001.

Una solución es la siguiente:

  1. Creamos una tabla de categorías, donde guardaremos las iniciales de los códigos (AAC, CAM, ESC, etc.). Esa columna debe tener la restricción Unique Key para que no tengamos códigos repetidos
  2. Creamos una tabla de bienes, donde se guardarán los códigos dependientes (AAC00001, AAC00002, AAC00003, etc.)
  3. A la columna donde guardaremos el código del bien le agregamos la restricción Unique Key
  4. En la tabla de bienes, creamos un trigger que se encargará de generar el código dependiente

CODIGO1

(haciendo clic en la imagen la verás más grande)

En la imagen de arriba podemos ver la estructura de la tabla CATEGORIAS. La columna CAT_INICIA debe tener la restricción Unique Key para asegurarnos que no tengamos iniciales repetidas.

CODIGO2

(haciendo clic en la imagen la verás más grande)

 En la imagen de arriba podemos ver algunas filas de la tabla CATEGORIAS.

CODIGO3

(haciendo clic en la imagen la verás más grande)

En la imagen de arriba podemos ver la estructura de la tabla BIENES. Recuerda que la columna BIE_CODIGO debe estar declararada como Unique Key para que el Firebird nos impida tener dos códigos idénticos.

A continuación, creamos un trigger como el siguiente:

SET TERM ^ ;

CREATE TRIGGER BIENES_BI FOR BIENES
   ACTIVE BEFORE INSERT
   POSITION 1
AS
   DECLARE VARIABLE lcCodigo D_CHAR8;
   DECLARE VARIABLE lnNumero D_CANTIDAD;
   DECLARE VARIABLE lcInicia D_CHAR3;
BEGIN

   /* Primero, se halla el último código de esta categoría */
   FOR SELECT
      BIE_CODIGO
   FROM
      BIENES
   WHERE
      BIE_IDECAT = NEW.BIE_IDECAT
   ORDER BY
      BIE_CODIGO
   INTO
      :lcCodigo
   DO BEGIN
   END

   /* Si no se encontró, el número será 1. Si se encontró, será el siguiente */
   IF (lcCodigo IS NULL) THEN
      lnNumero = 1;
   ELSE
      lnNumero = CAST(RIGHT(lcCodigo, 5) AS INTEGER) + 1;

   /* Se obtienen los caracteres iniciales del código */
   lcInicia = (SELECT CAT_INICIA FROM CATEGORIAS WHERE CAT_IDENTI = NEW.BIE_IDECAT);

   /* Se forma el código del bien que se grabará en la tabla */
   NEW.BIE_CODIGO = lcInicia || LPAD(lnNumero, 5, '0');

END^

SET TERM ; ^

Luego de insertar algunas filas en la tabla de BIENES esto es lo que obtenemos:

CODIGO4

(haciendo clic en la imagen la verás más grande)

Los usuarios solamente introdujeron el Identificador de la Categoría y el Nombre del Bien. El Identificador del Bien y el Código del Bien (la columna BIE_CODIGO) fueron puestos automáticamente por los triggers.

Observaciones:

  • En este ejemplo los códigos de los bienes empiezan con 3 caracteres predeterminados, pero pueden ser 2, 4, 5, ó cualquier otra cantidad. Eso se determina en la columna CAT_INICIA de la tabla CATEGORIAS
  • En este ejemplo los números tienen 5 dígitos y por lo tanto pueden variar entre 00001 y 99999 pero se pueden usar 2 dígitos, 3 dígitos, 7 dígitos o cualquier otra cantidad. Eso se determina en la columna BIE_CODIGO de la tabla BIENES. Como en este ejemplo la columna CAT_INICIA tiene 3 caracteres y el número máximo que se quiere usar es 99999 entonces la columna BIE_CODIGO tiene 8 caracteres.
  • Si más de una persona están introduciendo bienes al mismo tiempo entonces puede ocurrir que generen el mismo código del bien. Para evitar que se  graben códigos duplicados es que se debe usar la restricción Unique Key en la columna BIE_CODIGO. La primera persona grabará sin problemas, pero la segunda no podrá grabar porque el código ya existe y deberá volver a presionar el botón “Grabar” para que se genere un nuevo código del bien y allí sí se grabarán sus datos (bueno, si tiene mala suerte otra persona ya generó ese mismo código y deberá presionar el botón “Grabar” nuevamente hasta que tenga éxito. Si esta es una situación frecuente lo más conveniente es que sea el programa el encargado de reintentar la grabación hasta conseguirla).

Artículos relacionados:

https://firebird21.wordpress.com/2013/03/29/cast/

https://firebird21.wordpress.com/2013/04/10/lpad/

https://firebird21.wordpress.com/2013/04/14/right/

https://firebird21.wordpress.com/2013/03/17/entendiendo-a-los-triggers/

Obteniendo el nombre del día de la semana

1 comentario

A veces nos interesa saber si el nombre del día de una fecha es Lunes, Martes, Miércoles, etc. Para obtener esa información podríamos usar algo como:

SELECT
   DECODE(
      EXTRACT(
         WEEKDAY FROM MiColumnaFecha), 
            0, 'Domingo', 
            1, 'Lunes', 
            2, 'Martes', 
            3, 'Miércoles', 
            4, 'Jueves', 
            5, 'Viernes', 
            6, 'Sábado')
FROM
   MiTabla

WEEKDAY puede valer 0, 1, 2, 3, 4, 5, 6 dependiendo de si el día es Domingo, Lunes, Martes, Miércoles, Jueves, Viernes o Sábado

Por lo tanto al extraer el WEEKDAY con la  función EXTRACT() obtendremos uno de esos números.

Luego, con la función DECODE() según sea el número devolvemos el nombre del día. Si es 0, entonces el nombre del día es ‘Domingo’. Si es 1 entonces el nombre del día es ‘Lunes’ y así sucesivamente.

Como saber si una fecha es fin de semana

A veces, necesitamos saber si el nombre del día es Sábado o Domingo, por ejemplo si nuestros empleados trabajan de Lunes a Viernes entonces no deberían conectarse a la Base de Datos un Sábado o un Domingo. O si tenemos empleados que trabajan Sábados y Domingos y debemos pagarles más por ese motivo.

SELECT
   MiColumnaFecha
FROM
   MiTabla
WHERE
   EXTRACT(WEEKDAY FROM MiColumnaFecha) IN (0, 6)

En este caso, si el número de WEEKDAY es 0 (Domingo) o si es 6 (Sábado) la fecha cae en fin de semana.

Un stored procedure para obtener el nombre del día

Para ahorrarnos trabajo y no estar escribiendo la misma cosa una y otra vez, lo más sencillo es tener un stored procedure que nos devuelva el nombre del día de la semana, como el siguiente:

CREATE PROCEDURE NOMBRE_DIA(
   tdFechax DATE)
RETURNS(
   tcNombreDia VARCHAR(9))
AS
BEGIN

   tcNombreDia = (DECODE(
                     EXTRACT(
                        WEEKDAY FROM tdFechax), 
                           0, 'Domingo', 
                           1, 'Lunes', 
                           2, 'Martes', 
                           3, 'Miércoles', 
                           4, 'Jueves', 
                           5, 'Viernes', 
                           6, 'Sábado'));

   SUSPEND;

END;

Al cual podríamos llamar así:

EXECUTE PROCEDURE NOMBRE_DIA(CURRENT_DATE);

o si lo necesitamos dentro de un SELECT entonces así:

SELECT
   Nombre_Dia(MiColumnaFecha)
FROM
   MiTabla

Usando UPDATE OR INSERT

5 comentarios

Supongamos que queremos hacer lo siguiente:

  • Si existe una fila de una tabla, entonces cambiar el contenido de alguna columna
  • Si no existe esa fila, agregarla a la tabla

Esta es una tarea muy común en los programas llamados de ABM (agregar, borrar, modificar) o sea los programas que los usuarios utilizan para introducir datos a las tablas.

La forma antigua de realizar esa tarea sería esta:

SET TERM ^ ;

CREATE PROCEDURE GRABAR_BANCO(
   tnIdenti BIGINT,
   tcNombre VARCHAR(40))
AS
BEGIN

   IF (EXISTS(SELECT * FROM BANCOS WHERE BAN_IDENTI = :tnIdenti)) THEN
      UPDATE
         BANCOS
      SET
         BAN_NOMBRE = :tcNombre
      WHERE
         BAN_IDENTI = :tnIdenti;
   ELSE
      INSERT INTO BANCOS
             (BAN_IDENTI, BAN_NOMBRE)
      VALUES (:tnIdenti , :tcNombre );

END^

SET TERM ; ^

También podríamos escribirlo así, quizás sea más claro para algunos:

SET TERM ^ ;

CREATE PROCEDURE GRABAR_BANCO(
   tnIdenti BIGINT,
   tcNombre VARCHAR(40))
AS
BEGIN

   UPDATE
      BANCOS
   SET
      BAN_NOMBRE = :tcNombre
   WHERE
      BAN_IDENTI = :tnIdenti;

   IF (ROW_COUNT = 0) THEN
      INSERT INTO BANCOS
             (BAN_IDENTI, BAN_NOMBRE)
      VALUES (:tnIdenti , :tcNombre );

END^

SET TERM ; ^

En este caso, intentamos actualizar la tabla y si la variable ROW_COUNT es igual a cero significa que no lo conseguimos porque no había fila para actualizar, así que procedemos a insertar la fila.

En esos dos stored procedures se utilizó la forma antigua, la forma tradicional, la que puede utilizarse en cualquier motor SQL. Sin embargo, el Firebird nos provee de una mucha mejor alternativa: el comando UPDATE OR INSERT, el cual primero trata de actualizar una fila y si no lo consigue, la inserta.

SET TERM ^ ;

CREATE PROCEDURE GRABAR_BANCO(
   tnIdenti BIGINT,
   tcNombre VARCHAR(40))
AS
BEGIN

   UPDATE OR INSERT INTO BANCOS
          (BAN_IDENTI, BAN_NOMBRE)
   VALUES (:tnIdenti , :tcNombre );

END^

SET TERM ; ^

Como puedes ver, se ha escrito mucho menos y el código es más legible (bueno, al menos para mí es más legible). En este ejemplo la columna BAN_IDENTI es la Primary Key de la tabla BANCOS y el comando UPDATE OR INSERT intenta actualizar una fila que tenga esa Primary Key. Si no lo consigue (porque ninguna fila tiene esa Primary Key) entonces le inserta una fila a la tabla de BANCOS. Claro, fácil, sencillo, se escribe menos y se entiende mejor.

El archivo SECURITY2.FDB

1 comentario

Firebird guarda los nombres y las contraseñas de los usuarios en un archivo llamado SECURITY2.FDB y que se encuentra en la misma carpeta donde instalaste al Firebird. Por ejemplo, en la carpeta: C:\Archivos de Programa\Firebird\Firebird_2_5\

SECURITY2.FDB es una Base de Datos internamente igual a cualquier otra Base de Datos de Firebird pero con una gran diferencia: no es posible conectarse a ella. Ni siquiera el usuario SYSDBA puede hacerlo.

SECURITY2_1

 

(Haciendo clic en la imagen la verás más grande)

Como ves, el intento de conexión fue rechazado.

Sin embargo, hay un truco para hacerlo, si te interesa ver su contenido y eres curioso:

  1. Detienes el Servidor del Firebird (Inicio | Panel de control | Firebird Server Manager | Stop)
  2. Copias el archivo SECURITY2.FDB con otro nombre (Por ejemplo con el nombre SECURITY_COPIA.FDB)
  3. Reinicias el Servidor del Firebird (Inicio | Panel de control | Firebird Server Manager | Start)
  4. Te conectas a la Base de Datos SECURITY_COPIA.FDB

SECURITY2_2

 

(Haciendo clic en la imagen la verás más grande)

Con la Base de Datos renombrada, la conexión se realizó exitosamente.

Si queremos ver las tablas que tiene esta Base de Datos usamos el comando SHOW TABLES;

SECURITY2_3

 

(Haciendo clic en la imagen la verás más grande)

Esta Base de Datos tiene una sola tabla, cuyo nombre es RDB$USERS.

Para ver la estructura de esa tabla escribimos el comando SHOW TABLE RDB$USERS

SECURITY2_4

(Haciendo clic en la imagen la verás más grande)

También podemos conectarnos usando el programa EMS SQL Manager, pero siempre con la copia, nunca podremos conseguirlo si lo intentamos con la Base de Datos original.

SECURITY2_5

(Haciendo clic en la imagen la verás más grande)

 Al hacer doble clic sobre el nombre de la tabla vemos su estructura:

SECURITY2_6

 

(Haciendo clic en la imagen la verás más grande)

Y al hacer click sobre la pestaña Data vemos el contenido de esa tabla:

SECURITY2_7

 

(Haciendo clic en la imagen la verás más grande)

Como puedes ver, las contraseñas (passwords) de los usuarios están encriptadas. El algoritmo usado es muy eficiente y no podrás conocer cual es la contraseña de un usuario aunque lo intentes.

Recuerda que el archivo SECURITY2.FDB no se puede copiar ni borrar ni renombrar si el Servidor del Firebird está corriendo. Para que puedas realizar esas tareas el Servidor debe estar detenido (si no recuerdas como detenerlo puedes leer al principio de este artículo).

Normalmente nunca tendrías que hacer algo así, pero hay una excepción: cuando copias la Base de Datos que está en producción (o sea: a la que normalmente se conectan los usuarios) a otra computadora. En este caso, en lugar de estar volviendo a escribir los nombres y contraseñas de cada usuario (que pueden ser decenas o cientos) es mucho más rápido, práctico y fácil, copiar el archivo SECURITY2.FDB a la nueva computadora.