Entendiendo a GSTAT (4)

Deja un comentario

Entender como funciona el programa GSTAT es bastante largo. Ya hemos visto algo sobre él en estos artículos:

Entendiendo a GSTAT (1)

Entendiendo a GSTAT (2)

Entendiendo a GSTAT (3)

ahora, continuamos.

Opción -index

Esta es la opción que debemos elegir cuando solamente nos interesan los índices de nuestras tablas.

gstat01

Captura 1. Si haces clic en la imagen la verás más grande

Como es lo usual, la contraseña la extraemos de un archivo de texto y la salida del programa GSTAT la enviamos a otro archivo de texto. Esto último es para facilitarnos la tarea.

gstat02

Captura 2. Si haces clic en la imagen la verás más grande

En la Captura 2. vemos una parte del contenido del archivo de texto que nos muestra información sobre nuestros índices. Los nombres de los índices siempre aparecen ordenados alfabéticamente.

¿Cuál es el significado de lo que estamos viendo?

ASIENTOSCAB es el nombre de la tabla

131 es el identificador que la tabla ASIENTOSCAB tiene dentro de la tabla del sistema RDB$RELATIONS. Es en RDB$RELATIONS donde se  guardan los nombres de todas las tablas de la Base de Datos.

ASC01, ASC02, ASC03, y PK_ASIENTOSCAB son los nombres de los índices

0, 1, 2, 3, los números entre paréntesis que vemos después de los nombres de los índices son los identificadores de los índices menos 1 y nos indican el orden en el cual fueron creados, siendo 0 el primer índice que creamos, 1 el segundo índice que creamos, 2 el tercer índice que creamos y así sucesivamente. Si se creó un índice y luego se lo eliminó, el número que tenía no aparecerá en la lista.

Listado 1.

SELECT
   RDB$INDEX_ID,
   RDB$RELATION_NAME,
   RDB$INDEX_NAME
FROM
   RDB$INDICES
WHERE
   RDB$RELATION_NAME = 'ASIENTOSCAB'
ORDER BY
   RDB$INDEX_ID

gstat03

Captura 3. Si haces clic en la imagen la verás más grande

¿Por qué en la Captura 2. no muestra los verdaderos identificadores sino los identificadores menos 1? Es un verdadero misterio, quizás haya alguna razón valedera para ello pero es dudoso que exista. Probablemente sólo sea por costumbre.

Depth es la cantidad de indirecciones o desviaciones que hay en el árbol B-tree del índice. Lo ideal es que ese número sea como máximo 3. Si es mayor que 3 entonces el índice no será tan eficiente como podría ser ¿cómo lo solucionamos? aumentando el tamaño de la página de nuestra Base de Datos. Si por ejemplo el tamaño de la página es 4096 lo aumentamos a 8192 y volvemos a ejecutar a GSTAT con la opción -index para comprobar si Depth ahora es 1, 2, ó 3 (esos son los mejores valores que puede tener Depth). Si sigue siendo mayor que 3 entonces volvemos a aumentar el tamaño de la página de nuestra Base de Datos y ahora lo ponemos en 16384.

Leaf buckets es la cantidad de páginas que están en el nivel más bajo del árbol B-tree. Es en estas páginas donde se guardan los punteros a las filas de la tabla. En las demás páginas de índice se guardan los enlaces a otras páginas de índice.

Nodes es la cantidad total de filas en la tabla que han sido indexadas. Sin embargo, este número puede ser erróneo porque podrían aparecer filas que han sido borradas con DELETE y aún su basura no ha sido recolectada o también porque las columnas del índice cambiaron de valor. Por eso, es conveniente ejecutar a GSTAT con la opción -index solamente después de un sweep o de un ciclo backup/restore.

Average data length es el tamaño en bytes promedio de los datos de la columna (o columnas) que se indexaron. Como Firebird comprime esos datos antes de grabarlos en una página de índices, el average data length será siempre menor a la suma del tamaño de las columnas de la tabla.

Total dup es la cantidad total de duplicados que tiene un índice. Los índices que se utilizan en las restricciones Primary Key y Unique Key no admiten duplicados, pero los otros índices sí los admiten. Cuantos más duplicados haya, peor es el índice.

 Listado 2.

SELECT
   ASC_ANOEJE,
   ASC_CODSUC,
   ASC_NUMERO,
   COUNT(*)
FROM
   ASIENTOSCAB
GROUP BY
   ASC_ANOEJE,
   ASC_CODSUC,
   ASC_NUMERO

gstat05

Captura 4. Si haces clic en la imagen la verás más grande

gstat04

Captura 5. Si haces clic en la imagen la verás más grande

En la Captura 4. vemos que al ejecutar el Listado 2. se usó el índice ASC01, y en la Captura 5. vemos la cantidad de veces que los valores de ese índice aparecieron. En este caso no hay duplicados, ya que COUNT siempre nos muestra el número 1 pero en otros índices sí podría haber duplicados. El Listado 2. nos ayudará a conocer cuantos duplicados tiene nuestro índice. Recuerda que Total dup puede mostrarte una cantidad incorrecta de duplicados si no has hecho previamente un ciclo backup/restore.

Max dup es la cantidad máxima de valores duplicados que tiene un índice.

Fill distribution es una tabla de frecuencias y seguramente te recordarás de ellas si alguna vez estudiaste Estadísticas. Hay 5 filas, yendo de 20% en 20%, cada fila indicando la cantidad de páginas de índices que están completadas hasta ese porcentaje.

gstat02

Captura 6. Si haces clic en la imagen la verás más grande

En la Captura 6. vemos que hay 8 páginas de índices que están llenas entre un 20% y un 39%. Y hay 3 páginas de índices que están llenas entre un 40% y un 59%. La suma de 8 + 3 es 11, y siempre debe coincidir con leaf buckets.

Esta distribución es mala, estamos usando más páginas que las necesarias. ¿Cómo sabemos eso? porque no hay páginas rellenas entre un 80% y un 99%. En una buena distribución el número mayor debería estar siempre en la última fila, (es decir, en 80% a 99%). ¿Cómo conseguimos eso? Con un ciclo backup/restore y luego usando el backup restaurado.

Artículos relacionados:

Entendiendo a GSTAT (1)

Entendiendo a GSTAT (2)

Entendiendo a GSTAT (3)

El índice del blog Firebird21

El foro del blog Firebird21

Entendiendo a GSTAT (3)

Deja un comentario

Ya hemos visto algunas de las características del programa GSTAT en estos artículos:

Entendiendo a GSTAT (1)

Entendiendo a GSTAT (2)

ahora continuaremos analizándolo.

Opción -data

Esta es la opción que debemos elegir cuando solamente nos interesan las páginas de datos. Como recordarás, en Firebird todo se guarda dentro de páginas. En cada página se guarda solamente una clase de ítems, nunca jamás se mezclan. Así por ejemplo tenemos una página de cabecera, una o varias páginas para las transacciones, una o varias páginas para los datos, una o varias páginas para los índices, etc.

La opción -data nos informa sobre los datos que se encuentran dentro de nuestras tablas. Por ejemplo, si tenemos una tabla llamada ALUMNOS los nombres de los alumnos se encontrarán dentro de una o varias páginas de datos. Si tenemos una tabla llamada PROFESORES los nombres de los profesores se encontrarán dentro de una o varias páginas de datos. Pero las páginas donde se encuentran los nombres de los alumnos son distintas a las páginas donde se encuentran los nombres de los profesores. Es decir, en una página de datos podríamos tener nombres de alumnos, o nombres de profesores, pero jamás de ambos.

Eso significa que una tabla puede tener muchas páginas de datos, y que todas esas páginas le corresponden exclusivamente a esa tabla. Por ejemplo, los nombres de los ALUMNOS podrían encontrarse en las páginas 12.345, 12.346, 20.118, 20.129, y los nombres de los PROFESORES podrían encontrarse en las páginas 11.521 y 12.379, los números son siempre distintos.

gstat01

Captura 1. Si haces clic en la imagen la verás más grande

En la Captura 1. invocamos a GSTAT con la opción -data para que nos muestre información sobre los datos contenidos en nuestras tablas y, como es lo normal, enviamos esa información a un archivo de texto para poder analizarlo con más facilidad.

Abrimos ese archivo de texto con el Bloc de Notas del Windows y vemos lo siguiente (Nota: los nombres de nuestras tablas siempre aparecen ordenados alfabéticamente):

gstat02

Captura 2. Si haces clic en la imagen la verás más grande

¿Cuál es el significado de lo que estamos viendo?

ASIENTOSCAB es el nombre de nuestra tabla

(131) es el identificador de esta tabla en RDB$RELATIONS, lo cual podemos comprobar facilmente así:

gstat03

Captura 3. Si haces clic en la imagen la verás más grande

Como podemos ver en la Captura 3., el identificador 131 le corresponde a la tabla ASIENTOSCAB. En RDB$RELATIONS se guardan los nombres de todas las tablas que tiene la Base de Datos.

Primary pointer page es número de la primera página donde se guardan los punteros a las páginas de datos. ¿Qué significa eso? Bien, como ya sabemos, cada tabla puede tener varias páginas de datos y los números de esas páginas deben guardarse en algún lado. El lugar donde se guardan es en una Pointer Page. O sea que en una Pointer Page se encuentran los números de cada una de las páginas de datos de una tabla. El número 502 que vemos en la Captura 2. significa que en la página 502 se encuentran los números de las páginas de datos de la tabla ASIENTOSCAB. Las tablas grandes pueden necesitar de varias Pointer Page, la Primary pointer page es la primera de esas páginas. Por ejemplo, en la página 502 (que como ya sabemos es una pointer page) podríamos tener los números 31.218, 31.219, 92.128; eso significa que en las páginas cuyos números son 31.218, 31.219, y 92.128 se encuentran datos de la tabla ASIENTOSCAB.

Index root page es el número de la primera página donde se guardan los punteros a los índices. Así como tenemos páginas donde se guardan los datos de una tabla también tenemos páginas donde se guardan los índices de esa tabla. El número 503 que vemos en la Captura 2. significa que en la página número 503 se encuentran los punteros a las páginas de índices de la tabla ASIENTOSCAB. En otras palabras, los números que se encuentren en la página 503 serán los números de las páginas donde se encuentran los índices de la tabla ASIENTOSCAB. Si por ejemplo en la página 503 están los números 14.127 y 29.218 significa que en esas páginas hay índices que corresponden a la tabla ASIENTOSCAB.

Data pages es la cantidad total de páginas de datos de nuestra tabla. En la Captura 2. vemos que la tabla ASIENTOSCAB está usando 148 páginas de la Base de Datos. Hay que tener en cuenta algo importante aquí: algunas de esas páginas pueden contener basura dejada por los comandos UPDATE y DELETE, o tener datos de transacciones que aún no han finalizado con un COMMIT. O sea que no necesariamente todas las 148 páginas que usa ASIENTOSCAB contienen datos útiles.

Data page slots es la cantidad de punteros que hay en las pointer pages y frecuentemente es igual que data pages ¿por qué eso? porque cada página de datos tiene un puntero que guarda ese número de página. Si observamos la Captura 2. veremos que data pages es 148 y que data page slots también es 148. Eso es lo normal, y está muy bien que así sea, significa que la tabla ASIENTOSCAB usa 148 páginas de datos y que hay 148 punteros indicando cuales son esos números de página. Si los números son diferentes, eso no es un error. ¿qué significa que los números sean diferentes? Cada vez que el motor del Firebird necesita una nueva página para guardar los datos de una tabla guarda el número de esa página en una pointer page de esa tabla. Si más adelante esa página que tenía datos ya no los tiene porque un comando DELETE los borró, el puntero que guardaba  ese número de página permanece en la pointer page, no es borrado de allí. ¿Por qué no es borrado? porque si más adelante la tabla necesita una nueva página de datos usará (de ser posible) una página de datos que ya había usado anteriormente. La finalidad de hacerlo así es que eso acelera el proceso. O sea, es mejor reusar una página de datos vacía que crear una nueva página de datos.

gstat04

Captura 4. Si haces clic en la imagen la verás más grande

En la Captura 4. vemos que la página número 502 es una pointer page, que corresponde a la tabla ASIENTOSCAB y que contiene los números de las páginas de datos que tienen datos de la tabla ASIENTOSCAB. O sea que la página número 31.218 es una página de datos y los datos allí contenidos corresponden a la tabla ASIENTOSCAB. Lo mismo se aplica a los demás números que vemos en la segunda columna.

gstat05

Captura 5. Si haces clic en la imagen la verás más grande

En la Captura 5. vemos las estadísticas de la tabla CUENTAS y observamos que data pages es distinto que data page slots. ¿Qué significa eso? que alguna vez la tabla CUENTAS tuvo 85 páginas de datos pero ahora solamente está usando 15 de esas páginas, las restantes 70 están vacías, sin datos, pero disponibles para ser re-utilizadas cuando la tabla CUENTAS las necesite. Esas páginas que ahora están vacías alguna vez contuvieron datos pero esos datos fueron borrados con un comando DELETE o cuando se recolectó la basura. Si una página solamente contenía basura la recolección automática de basura o un sweep podrían eliminar a esa basura pero no liberan a la página, esa página sigue perteneciendo a la misma tabla. Cuando un comando INSERT o un comando UPDATE o un comando DELETE necesite de una nueva página de datos para escribir lo que necesita escribir, se usará una de esas páginas de datos que ahora están vacías.

gstat06

Captura 6. Si haces clic en la imagen la verás más grande

En la Captura 6. vemos que la tabla CUENTAS tiene una pointer page cuyo número es 498 y que nos indica cuales de sus páginas de datos contienen datos y cuales están vacías. Esas páginas de datos que ahora están vacías se usarán alguna vez si la tabla CUENTAS necesita más páginas de datos. ¿Y cuándo la tabla CUENTAS podría necesitar más páginas de datos? Cuando las páginas que está usando ahora se llenen. Una página podría llenarse porque el comando INSERT ha agregado nuevas filas o porque los comandos UPDATE o DELETE han agregado versiones viejas de una fila. En general, el comando INSERT llena una página hasta un 80% de su capacidad y deja un 20% libre para que los comandos UPDATE y DELETE coloquen allí las versiones antiguas de las filas. ¿Por qué hace eso? Para acelerar el proceso, porque es más rápido guardar las versiones viejas de una fila en la misma página de datos que contiene a esa fila que usar una nueva página de datos. Por ejemplo, si la fila cuyo identificador es 12.345 se encuentra en la página número 32.334 y se hace un UPDATE o un DELETE a la fila con identificador 12.345 lo más conveniente es que las versiones viejas de esa fila también se guarden en la página número 32.334, eso es más rápido que guardar las versiones viejas en otra página de datos.

Mirando los números de data page slots y de data pages podemos saber cuantas páginas de nuestra tabla contienen solamente basura y por lo tanto están disponibles para ser utilizadas. Según la Captura 5. vemos que data page slots es 85 y que data pages es 15, eso implica que hay 70 páginas de datos vacías y disponibles.

Un ciclo backup/restore hace que ambos números sean iguales. En el backup restaurado la cantidad de data pages será siempre igual a la cantidad de data page slots.

Average fills es el promedio de llenado de las páginas de datos, e incluye a las versiones viejas de las filas (las versiones viejas son creadas por los comandos UPDATE y DELETE). Mirando la fill distribution tendremos más detalles.

Fill distribution es una tabla de frecuencias. Si alguna vez estudiaste Estadísticas seguramente las recordarás.

gstat02

Captura 7. Si haces clic en la imagen la verás más grande

Esta tabla de frecuencias tiene 5 filas, que van de 20% en 20% y sirve para indicarnos como están llenadas las páginas de datos.

Mirando la Captura 7. descubrimos que hay 97 páginas de datos que están llenas entre un 0% y un 19%, o sea que en esas 97 páginas de datos hay muchísimo espacio libre.

Hay 5 páginas de datos que están llenas entre un 20% y un 39%

Hay 20 páginas de datos que están llenas entre un 40% y un 59%

Hay 3 páginas de datos que están llenas entre un 60% y un 79%

Hay 23 páginas de datos que están llenas entre un 80%  y un 99%, o sea que hay muy poco espacio libre en ellas

La suma de esas cantidades de páginas siempre debe ser igual a data pages. Veamos si es así: 97 + 5 + 20 + 3 + 23 = 148. Está perfecto, así debe ser.

Artículos relacionados:

Entendiendo a GSTAT (1)

Entendiendo a GSTAT (2)

El índice del blog Firebird21

El foro del blog Firebird21

Enviando una consulta a EXCEL

2 comentarios

Hay muchas formas de enviar el resultado de una consulta a una planilla Excel, aquí veremos una de esas formas.

NOTA: el código fuente está basado en Visual FoxPro pero puede ser fácilmente adaptado a otros lenguajes de programación.

La ventaja de este método es que nos resultará muy fácil enviar la consulta que queremos, al libro Excel que queremos, a la hoja Excel que queremos, en el rango que queremos mostraremos la consulta, y esa consulta puede estar basada en cualquier tabla o en cualquier vista de nuestra Base de Datos del Firebird.

Primero, creamos la función que se encargará de realizar la tarea.

Listado 1.

FUNCTION ENVIAR_EXCEL_CON_DSN
LParameters tcDSN, tcUsuario, tcPassword, tcConsulta, tcRango
Local lcMensajeError, lcConsulta, loExcel, loLibro, loHoja, loTabla, lcConexion, lcBaseDatos
   
  lcMensajeError = ""
  
  tcRango = iif(VarType(tcRango) <> "C", "A1", tcRango)    && Si no se definió donde colocar la tabla, se la coloca en la celda A1
  
  *-- Tratamos de crear un objeto Excel
  if Empty(lcMensajeError)
    loExcel = CreateObject("Excel.Application")
    lcMensajeError = iif(VarType(loExcel) <> "O", "No se pudo crear el objeto EXCEL, verifica si está instaldo EXCEL", lcMensajeError)
  endif
  
  *-- Si todo OK, tratamos de crear un libro Excel
  if Empty(lcMensajeError)
    loLibro = loExcel.Workbooks.Add
    lcMensajeError = iif(VarType(loLibro) <> "O", "No se pudo crear el libro de EXCEL", lcMensajeError)
  endif
  
  *-- Si todo OK, tratamos de crear una hoja en el libro Excel
  if Empty(lcMensajeError)
    loHoja = loLibro.Worksheets(1)
    lcMensajeError = iif(VarType(loHoja) <> "O", "No se pudo crear la hoja en el libro EXCEL", lcMensajeError)
  endif
  
  *-- Si todo OK, creamos el string de conexión a la Base de Datos del Firebird, usando el DSN de esa Base de Datos
  if Empty(lcMensajeError)
    lcConexion = "ODBC;DSN=" + tcDSN + ";USER=" + tcUsuario + ";PASSWORD=" + tcPassword
  endif
  
  *-- Si todo OK, tratamos de conectarnos a la Base de Datos del Firebird
  if Empty(lcMensajeError)
    loTabla = loHoja.QueryTables.Add(lcConexion, loHoja.Range(tcRango), tcConsulta)
    lcMensajeError = iif(VarType(loTabla) <> "O", "No se pudo crear la tabla de consulta en la planilla Excel", lcMensajeError)
  endif
  
  *-- Si todo OK, insertamos la tabla en la planilal Excel
  if Empty(lcMensajeError)
    #DEFINE xlInsertEntireRows 2
    loTabla.RefreshStyle = xlInsertEntireRows
    loTabla.Refresh
  endif
  
  *-- Si todoOK, mostramos la planilla Excel
  if Empty(lcMensajeError)
    loExcel.Visible = .T.
  endif
  
  *-- Liberamos de la memoria los objetos que habíamos creado
  loTabla = NULL
  loHoja  = NULL
  loLibro = NULL
  loExcel = NULL
  
Return(lcMensajeError)
*
*

Y después llamamos a esa función, con los parámetros que deseemos usar.

Listado 2.

Local lcDSN, lcUsuario, lcPassword, lcConsulta, lcMensajeError, lcRango
  
  lcDSN = "AUTOMUNDO-FIREBIRD"
  
  lcUsuario = "SYSDBA"
  lcPassword = "masterkey"
  
   lcConsulta = "SELECT " ;
              + "   ASC_ANOEJE, " ;
              + "   ASC_CODSUC, " ;
              + "   ASC_FECHAX, " ;
              + "   ASC_NUMERO " ;
              + "FROM " ;
              + "   V_CONTA_ABM_ASIENTOS " ;
              + "WHERE " ;
              + "   ASC_NUMERO >= 100 AND " ;
              + "   ASC_NUMERO <= 120"
  
  lcRango = "B5"
  
  lcMensajeError = ENVIAR_EXCEL_CON_DSN(lcDSN, lcUsuario, lcPassword, lcConsulta, lcRango)
  
  *-- Si ocurrió algún error, mostramos un mensaje al usuario
  if !Empty(lcMensajeError)
    #DEFINE MSG_ICONO_ERROR 16
    #DEFINE MSG_BOTON_ACEPTAR 0
    =MessageBox(lcMensajeError, MSG_ICONO_ERROR + MSG_BOTON_ACEPTAR, "Hay un problema...")
  endif
  
Return
*
*

Y así obtendremos una planilla similar a la siguiente:

EXCEL01

Captura 1. Si haces clic en la imagen la verás más grande

Por lo tanto, nos resultará muy sencillo crear una planilla Excel con los datos que el usuario pueda necesitar. Inclusive podríamos tener en nuestra aplicación una opción que le muestre todas las vistas disponibles y que le permita crear consultas personalizadas, a su gusto y paladar. Ya depende de la creatividad de cada uno hacer algo así.

Artículos relacionados:

Usando EXCEL para leer bases de datos de Firebird

Planillas EXCEL dinámicas con Firebird (1)

Planillas EXCEL dinámicas con Firebird (2)

El índice del blog Firebird21

El foro del blog Firebird21

 

Copiando completamente o parcialmente una Base de Datos (1)

1 comentario

A veces puedes querer copiar una Base de Datos completa, con todo su contenido, pero a veces puede interesarte copiar solamente una parte de ella. Por lo tanto, tus opciones serían:

  1. Copiarla totalmente. Con todos sus metadatos y todos sus datos
  2. Copiar solamente los metadatos. Es decir, su estructura
  3. Copiar todos los metadatos y el contenido de algunas tablas
  4. Copiar algunos metadatos y el contenido de algunas tablas

Como el tema es largo, será tratado en 2 artículos. En este veremos como realizar esas tareas manualmente, o sea sin usar un programa de administración gráfica y en el siguiente artículo usaremos un programa de administración gráfica.

Opción 1. Copiarla totalmente. Con todos sus metadatos y todos sus datos

Para este caso lo más recomendable es hacer un backup usando el programa GBAK. ¿Por qué es lo más recomendable? porque al hacer un backup con GBAK se elimina la basura que contenía la Base de Datos original y en el backup no hay basura, ni índices (que serán creados cuando se restaure el backup), y el identificador de todas las transacciones del backup es puesto en cero.


GBAK -b MiBaseDatos.FDB MiBackup.FBK -user SYSDBA -password masterkey

Si el Servidor del Firebird no está funcionando también se puede copiar la Base de Datos con el Explorador de Windows o algún programa similar. ¿Por qué el Servidor del Firebird no debe estar funcionando? Porque si lo está y alguien está conectado, la Base de Datos puede corromperse. Y si nadie está conectado existe el riesgo (pequeño, pero existe) de que el Servidor esté realizando alguna tarea en la Base de Datos (por ejemplo, un sweep). Por lo tanto, copiar con el Explorador del Windows solamente es seguro si el Servidor del Firebird está apagado.

Opción 2. Copiar solamente los metadatos. Es decir, su estructura

El programa GBAK tiene la opción -m la cual copia solamente los metadatos. Su sintaxis es:


GBAK -b -m MiBaseDatos.FDB MiBackup.FBK -user SYSDBA -password masterkey

Opción 3. Copiar todos los metadatos y el contenido de algunas tablas

Para este caso lo mejor es crear un archivo de script con el contenido completo de la Base de Datos y luego eliminar lo que no nos interesa, modificar lo que queremos cambiar, y dejar como está a lo demás.

El programa ISQL tiene una opción -extract que sirve para crear un script de toda la Base de Datos.

COPIAR04

Captura 1. Si haces clic en la imagen la verás más grande

En la Captura 1. vemos que todo el contenido de la Base de Datos DEISY.FDB fue copiado al archivo de script llamado DEISY.SQL

COPIAR05

Captura 2. Si haces clic en la imagen la verás más grande

El archivo de script DEISY.SQL por supuesto que es muchísimo más largo, en la Captura 2. se muestran solamente las primeras líneas.

Ahora, tenemos la posibilidad de eliminar lo que ya no nos interesa tener y de modificar cualquier cosa. Podemos cambiar el nombre o el contenido de los dominios, de las tablas, de los stored procedures, etc. Lo que se nos ocurra.

Después de haber eliminado y modificado todo lo que quisimos, para generar una nueva Base de Datos, debemos quitarle el comentario a la línea CREATE DATABASE, poner el nombre que tendrá la nueva Base de Datos, y el usuario y la contraseña que la creará.

COPIAR06

Captura 3. Si haces clic en la imagen la verás más grande

Para crear la Base de Datos llamada DEISY2.FDB, ingresamos a ISQL y con el comando INPUT ejecutamos el script. Todo lo que esté escrito dentro del script será ejecutado.

COPIAR07

Captura 4. Si haces clic en la imagen la verás más grande

Si todo está bien, la nueva Base de Datos será creada. Si hay algún error, habrá que encontrar cual es el error y corregirlo. Luego, se vuelve a intentar crear la Base de Datos.

Copiando solamente el contenido de algunas tablas

Hasta ahora hemos visto como copiar a una Base de Datos completa, con todos sus metadatos y todos sus datos. Pero ¿y si queremos copiar solamente el contenido de algunas tablas?

Para eso entramos a ISQL, nos conectamos a una Base de Datos, y usando el comando OUTPUT enviamos a un archivo de texto todo lo que hacemos, tal y como vemos a continuación:

COPIAR01

Captura 5. Si haces clic en la imagen la verás más grande

La primera línea le indica al ISQL que todo lo que se escriba a continuación se guarde en un archivo de texto. No se verá en la pantalla, sino que se guardará en el archivo cuyo nombre se escriba después de la palabra OUTPUT.

Ese nombre puede ser cualquiera y puede tener cualquier extensión. Para los datos se acostumbra a ponerle la extensión .DAT pero eso no es obligatorio.

El OUTPUT que se encuentra en la tercera línea se usa para que la salida vuelva a verse en la pantalla, o sea que dejará de enviarse al archivo de texto.

Entre el primer OUTPUT y el segundo OUTPUT puede haber muchas líneas, no solamente una como en este ejemplo.

En el contenido del archivo de texto BANCOS.DAT se verá algo como:

COPIAR02

Captura 6. Si haces clic en la imagen la verás más grande

Donde, como puedes ver, se muestra el nombre de cada columna y el contenido de cada fila.

Opción 4. Copiar algunos metadatos y el contenido de algunas tablas

Es igual a la Opción 3.

Conclusión:

Como has podido ver en este artículo, con Firebird tenemos la posibilidad de realizar backups completos o parciales de nuestra Base de Datos.

Enviar el backup a un archivo de script tiene la gran ventaja de que todo es legible y así podríamos revisar ese script y descubrir errores o mejoras que se podrían realizar.

Artículos relacionados:

Entendiendo a los scripts

Usando scripts para documentar la Base de Datos

Ejecutando un script desde Visual FoxPro

El índice del blog Firebird21

El foro del blog Firebird21

Almacenamiento de las columnas de tipo BLOB

Deja un comentario

En Firebird tenemos la posibilidad de almacenar (guardar) archivos dentro de las bases de datos. Podemos usar esta característica para guardar documentos de texto, hojas de cálculo, gráficos, fotografías, canciones, vídeos, etc.

Para ello, declaramos a la columna como de tipo BLOB (Binary Large OBjets), o sea: “objetos binarios de gran tamaño”.

¿Dónde se guardan esos objetos binarios de gran tamaño?

Si declaramos a una columna como siendo de tipo CHAR, VARCHAR, SMALLINT, INTEGER, etc., es en esa misma columna donde se guardan sus valores. Pero con las columnas de tipo BLOB no sucede así. En este caso en la columna se guarda un puntero (un número que indica una dirección) a la ubicación física del archivo BLOB.

(Algo similar ocurre en los xBase: dBase, Visual FoxPro, etc., allí se les llama “campos memo” y se guardan en archivos distintos a las tablas .DBF)

Firebird hace lo siguiente:

  • Si el archivo BLOB cabe en la misma página que su fila (registro), se guarda en esa página. Recuerda que los tamaños de página pueden ser de 4.096, 8.192, 16.384 bytes. Como en general los archivos BLOB tienen un tamaño mayor a 16.384 bytes entonces es muy raro que sean guardados en la misma página que su fila respectiva.
  • Si el archivo BLOB no cabe en la misma página que su fila (registro) entonces se guarda en otra o en otras páginas. Estas páginas son del tipo “overflow”. Recordarás que todo en Firebird se guarda dentro de páginas. Los archivos BLOB no son la excepción. Esas “páginas de overflow” siempre están relacionadas con una tabla y por lo tanto son localizadas a través de las filas (registros) de esa tabla. El Firebird sabe a cual tabla pertenece cada “página de overflow” porque en la cabecera de la “página de overflow” coloca ese dato, así también como la fila (registro) que le corresponde y la secuencia en que se encuentra (si un archivo BLOB ocupa varias páginas el Firebird debe poder saber cual es la primera página, cual la segunda, cual la tercera, etc.)

Conclusión:

Las columnas de tipo BLOB normalmente se utilizan para guardar archivos dentro de ellas. Esos archivos como todo en Firebird se guardan dentro de páginas de la Base de Datos. Si el archivo BLOB es pequeño entonces podría guardarse en la misma página que su fila respectiva, pero lo normal es que el archivo BLOB sea más grande que el tamaño de una página y en ese caso se guarda en páginas llamadas de “overflow”. En la cabecera de las páginas de overflow se encuentran los datos que permiten saber a cual  tabla pertenecen, a cual fila de esa tabla, y la posición de esa página con relación a las demás páginas de ese archivo BLOB.

Artículos relacionados:

El índice del blog Firebird21

El foro del blog Firebird21

Encontrando filas duplicadas

2 comentarios

En una Base de Datos bien diseñada nunca deberían existir filas duplicadas (o triplicadas, o cuadruplicadas, o…) pero a veces algo así sucede y debemos en primer lugar encontrar el motivo o la causa que las permitió y en segundo lugar eliminarlas.

¿Por qué hay que eliminarlas?

Porque si están duplicadas están sobrando, están de más, están aunque no deberían estar, y el tenerlas dentro de la Base de Datos solamente nos puede causar problemas y ningún beneficio: los informes tendrán más filas de las que deberían tener, los totales serán mayores a los reales, etc.

 ¿Y por qué causa tenemos filas duplicadas?

Si nuestra Base de Datos está bien diseñada sería imposible que tal cosa ocurra si los usuarios introducen los datos manualmente pero a veces los datos provienen de fuentes externas como un archivo de texto, una planilla Excel, una tabla .DBF, etc.

Y si está mal diseñada permitirá filas duplicadas. Un caso insidioso puede ocurrir cuando tenemos dos o más formularios que se utilizan para introducir datos en las tablas. Por ejemplo, una aplicación de Contabilidad tiene un módulo de Clientes y otra aplicación de Facturación también tiene un módulo de Clientes. Los usuarios pueden introducir datos de los clientes tanto desde la Contabilidad como desde la Facturación, algo así puede ser problemático de solucionar, con mayor razón cuando los programadores de ambas aplicaciones son distintas personas.

Y es en esos casos, en los cuales no tuvimos control cuando se insertaron las filas (porque provienen de un archivo de texto, una planilla Excel, una tabla .DBF, etc), o nuestro control fue mediocre, en que debemos verificar cuidadosamente que no existan filas duplicadas.

 Problemas que causan las filas duplicadas

  1. Los cálculos son incorrectos porque algunas filas son contadas más de una vez y deberían contarse una sola vez. Por lo tanto los informes serán incorrectos.
  2. Pueden existir datos incompletos en una fila, porque están en otra fila
  3. La velocidad de las operaciones en la Base de Datos (insertar, modificar, borrar, consultar) disminuye porque cuanto más grande es una Base de Datos más lentas son esas operaciones. Si hay filas duplicadas el tamaño de la Base de Datos será mayor que el necesario y por lo tanto las operaciones serán más lentas de lo que deberían ser
  4. Eliminar las filas duplicadas toma tiempo, no es algo instantáneo, y el personal desperdicia ese tiempo en encontrar y eliminar las filas duplicadas en lugar de dedicarlo a algo más importante

¿Cómo encontrar filas duplicadas?

Para estos casos lo mejor es crear un stored procedure seleccionable que se dedique a esa tarea, dicho stored procedure es muy sencillo, simplemente tendrá un FOR SELECT por cada tabla que nos interesa, algo como:

CREATE PROCEDURE SP_ENCONTRANDO_DUPLICADOS
   RETURNS(
      ftcNombre VARCHAR(255))
AS
   DECLARE VARIABLE lnCantidad INTEGER;
BEGIN

   FOR SELECT
      DEP_NOMBRE,
      COUNT(*)
   FROM
      DEPARTAMENTOS
   GROUP BY
      DEP_NOMBRE
   HAVING
      COUNT(*) > 1
   INTO
      :ftcNombre,
      :lnCantidad
   DO BEGIN
      SUSPEND;
   END

END;

Aquí hay una sola tabla, llamada DEPARTAMENTOS, y lo que se verifica es si el nombre de algún departamento está duplicado. Si ese es el caso entonces la condición COUNT(*) > 1 será verdadera y el nombre del Departamento será mostrado.

Un stored procedure más completo tendría varios FOR SELECT, uno por cada tabla que nos interesa verificar.

Lo importante a recordar es que no debemos permitir jamás, por ningún motivo, y bajo ninguna circunstancia, que en nuestras tablas existan filas duplicadas, porque si existen eso solamente podrá acarrearnos problemas.

Artículos relacionados:

Averiguando si hay datos duplicados

El índice del blog Firebird21

El foro del blog Firebird21

Evitando que el tamaño de la Base de Datos se incremente demasiado

4 comentarios

Como Firebird usa la arquitectura MGA el tamaño de sus bases de datos puede crecer mucho y muy rápido.

La arquitectura MGA es muy buena para nosotros porque nos permite regresar la Base de Datos a su estado anterior. Cada vez que se modifica o se borra una fila esta arquitectura guarda una copia de la fila original. De esa manera siempre tenemos disponible la última versión de la fila y la ante-última versión de la fila. Y con los comandos COMMIT y ROLLBACK decidimos cual de esas versiones queremos usar.

En algunas transacciones nuestros cambios afectan a solamente una fila, pero en otras transacciones podrían afectar a decenas, miles, o millones de filas. Y gracias a MGA no debemos preocuparnos porque si queremos retrotraer la Base de Datos a su estado anterior, un simple comando ROLLBACK hará la tarea por nosotros.

Pero no todo son ventajas, también MGA tiene sus desventajas. Y es que esas filas que se fueron agregando con cada comando UPDATE o DELETE que ejecutamos van ocupando espacio dentro de la Base de Datos haciendo que se vuelva cada vez más grande. Y al volverse más grande también muchas operaciones se vuelven más lentas. El conjunto de todas esas filas que están dentro de la Base de Datos pero que ya son totalmente inútiles se llama “basura”.

La buena noticia es que podemos eliminar a la basura muy fácilmente. La mala noticia es que requiere de tiempo, no es algo instantáneo. Como consecuencia, lo recomendable es que la basura sea eliminada cuando nadie usa la Base de Datos. Si esto no es posible, entonces se debe eliminarla el día o la hora en que menos usuarios estén conectados (por ejemplo, todos los Domingos a las 23:00)

Entonces, nuestra tarea de mantenimiento tiene 2 partes:

  1. Un ciclo backup/restore
  2. Un barrido de la basura

El ciclo backup/restore nos asegura que solamente la última versión de cada fila se encuentra en la Base de Datos. Por lo tanto su tamaño disminuirá. Y en ocasiones la disminución es asombrosa, una Base de Datos de 350 Mb podría pasar a tener 40 Mb cuando el ciclo finaliza.

El barrido de la basura nos permitirá evitar que el tamaño de la Base de Datos crezca sin control. Si periódicamente hacemos un barrido entonces siempre tendremos una Base de Datos en muy buen estado de salud. Podríamos barrer un vez al día, o quizás una vez a la semana, ya dependerá de la cantidad de transacciones que durante ese tiempo ocurrieron. Un método es el siguiente:

  1. Detener el Servidor del Firebird
  2. Reiniciar el Servidor del Firebird (no debería durar ni 5 segundos el ciclo detener/reiniciar)
  3. Abrir la ventanita “Símbolo del sistema”
  4. Ejecutar el comando: GFIX -sweep -user SYSDBA -password masterkey MiBaseDatos

Realizar un ciclo backup/restore puede ser muy complicado en las empresas que trabajan 24/7/365, en cambio el barrido de la basura es más factible de encontrar el momento adecuado para hacerlo.

Sea como sea, mantener la Base de Datos con poca o nada de basura es una tarea muy importante, que está implícita cuando se trabaja con Firebird, y que debe realizarse sí o sí, no hay excusas, al menos si queremos que los tiempos de respuesta que obtienen los usuarios sean aceptables.

Artículos relacionados:

La arquitectura MGA

Entendiendo sweep y garbage collection

El índice del blog Firebird21

El foro del blog Firebird21